feat: merge teacher

This commit is contained in:
莘权 马 2023-08-22 22:34:39 +08:00
commit 01c487fb6a
32 changed files with 1578 additions and 38 deletions

5
.gitignore vendored
View file

@ -163,4 +163,9 @@ workspace/*
*.mmd
tmp
output.wav
*.bak
# output folder
output
tmp.png

View file

@ -0,0 +1,40 @@
# Pattern Configuration Template
# Created By: mashenquan, 2023-8-7
# File Name: template.yaml
# This template defines a set of structural standards for generating roles and action flows based on configurations.
# For more about UML 2.0 activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
# project settings
startup:
requirement: "TeachingPlanRequirement" # Defines project initial requirement action
role: "Teacher" # Defines project role
investment: 3.0 # Defines the max project investment
n_round: 1 # Defines the max project round count
# roles settings
roles: # A project can involve multiple roles.
- role_type: "fork" # `fork` type role corresponds to the functional positioning of the `fork` node in UML 2.0 activity diagrams.
name: "Lily"
profile: "{teaching_language} Teacher"
goal: "writing a {language} teaching plan part by part"
constraints: "writing in {language}"
role: "You are a {teaching_language} Teacher, named Lily, your goal is ..."
desc: ""
output_filename: "teaching_plan_demo.md"
requirement: ["TeachingPlanRequirement"]
templates: # The template provides a convenient way to generate prompts. After each action selects its respective template, you only need to provide the corresponding variable values. Variable replacement is automatically handled by the framework.
- "Do ..."
- "Do ..."
# role's action settings
actions: # A role can have multiple actions.
- name: ""
topic: "Title"
language: "Chinese"
statements: # When replacing template variables, multiple statements will be joined into a single string using line breaks.
- "Statement: Find and return ..."
template_ix: 0
rsp_begin_tag: "[..._BEGIN]" # When asking, request the LLM to include the tag in the response. It's optional.
rsp_end_tag: "[..._END]" # When asking, request the LLM to include the tag in the response. It's optional.

View file

@ -0,0 +1,126 @@
# The `fork` role demo implements the flow of the code in `examples/write_teaching_plan.py`.
# project settings
startup:
requirement: "TeachingPlanRequirement" # Defines project initial requirement action
role: "Teacher"
investment: 3.0
n_round: 1
# roles settings
roles: # A project can involve multiple roles.
- role_type: "fork" # `fork` type role corresponds to the functional positioning of the `fork` node in UML 2.0 activity diagrams.
name: "Lily"
profile: "{teaching_language} Teacher"
goal: "writing a {language} teaching plan part by part"
constraints: "writing in {language}"
role: "You are a {teaching_language} Teacher, named Lily, your goal is writing a {teaching_language} teaching plan part by part, and the constraint is writing in {language}."
desc: ""
output_filename: "teaching_plan_demo"
requirement: ["TeachingPlanRequirement"]
templates: # The template provides a convenient way to generate prompts. After each action selects its respective template, you only need to provide the corresponding variable values. Variable replacement is automatically handled by the framework.
- "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\n{statements}\nConstraint: Writing in {language}.\nAnswer options: Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END]"
- "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: {role}\nStatement: Write the \"{topic}\" part of teaching plan, WITHOUT ANY content unrelated to \"{topic}\"!!\n{statements}\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in {language}.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END]"
actions: # 一个role可以有多个action
- name: ""
topic: "Title"
language: "Chinese"
statements: # When replacing template variables, multiple statements will be joined into a single string using line breaks.
- "Statement: Find and return the title of the lesson only with \"# \" string prefixed, without anything else."
template_ix: 0
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Teaching Hours"
language: "Chinese"
statements: []
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]" # When asking, request the LLM to include the tag in the response. It's optional.
rsp_end_tag: "[TEACHING_PLAN_END]" # When asking, request the LLM to include the tag in the response. It's optional.
- name: ""
topic: "Teaching Objectives"
language: "Chinese"
statements: []
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Teaching Content"
language: "Chinese"
statements:
- "Statement: \"Teaching Content\" must include vocabulary, analysis, and examples of various grammar structures that appear in the textbook, as well as the listening materials and key points."
- "Statement: \"Teaching Content\" must include more examples."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Teaching Methods and Strategies"
language: "Chinese"
statements:
- "Statement: \"Teaching Methods and Strategies\" must include teaching focus, difficulties, materials, procedures, in detail."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Learning Activities"
language: "Chinese"
statements: []
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Teaching Time Allocation"
language: "Chinese"
statements:
- "Statement: \"Teaching Time Allocation\" must include how much time is allocated to each part of the textbook content."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Assessment and Feedback"
language: "Chinese"
statements: []
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Teaching Summary and Improvement"
language: "Chinese"
statements: []
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Vocabulary Cloze"
language: "Chinese"
statements:
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create vocabulary cloze. The cloze should include 10 {language} questions with {teaching_language} answers, and it should also include 10 {teaching_language} questions with {language} answers. The key-related vocabulary and phrases in the textbook content must all be included in the exercises."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Choice Questions"
language: "Chinese"
statements:
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create choice questions. 10 questions."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Grammar Questions"
language: "Chinese"
statements:
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create grammar questions. 10 questions."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"
- name: ""
topic: "Translation Questions"
language: "Chinese"
statements:
- "Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create translation questions. The translation should include 10 {language} questions with {teaching_language} answers, and it should also include 10 {teaching_language} questions with {language} answers."
template_ix: 1
rsp_begin_tag: "[TEACHING_PLAN_BEGIN]"
rsp_end_tag: "[TEACHING_PLAN_END]"

View file

@ -0,0 +1,126 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/7
@Author : mashenquan
@File : fork_meta_role.py
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to possess the
ability to construct flows freely by concatenating symbols. Simultaneously, I am also striving to make
these symbols configurable and standardized, making the process of building flow structures more
convenient. This is a fork meta-role demo that implements the functionality of
`examples/write_teaching_plan.py`.
"""
import asyncio
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parent.parent))
import aiofiles
import fire
import yaml
from metagpt.actions.meta_action import MetaAction
from metagpt.logs import logger
from metagpt.roles.uml_meta_role_factory import UMLMetaRoleFactory
from metagpt.roles.uml_meta_role_options import ProjectConfig
from metagpt.software_company import SoftwareCompany
async def startup(lesson_file: str, investment: float = 3.0, n_round: int = 1, *args, **kwargs):
"""Run a startup. Be a teacher in education industry."""
demo_lesson = """
UNIT 1 Making New Friends
TOPIC 1 Welcome to China!
Section A
1a Listen and number the following names.
Jane Mari Kangkang Michael
Look, listen and understand. Then practice the conversation.
Work in groups. Introduce yourself using
I m ... Then practice 1a
with your own hometown or the following places.
1b Listen and number the following names
Jane Michael Maria Kangkang
1c Work in groups. Introduce yourself using I m ... Then practice 1a with your own hometown or the following places.
China the USA the UK Hong Kong Beijing
2a Look, listen and understand. Then practice the conversation
Hello!
Hello!
Hello!
Hello! Are you Maria?
No, Im not. Im Jane.
Oh, nice to meet you, Jane
Nice to meet you, too.
Hi, Maria!
Hi, Kangkang!
Welcome to China!
Thanks.
2b Work in groups. Make up a conversation with your own name and the
following structures.
A: Hello! / Good morning! / Hi! Im ... Are you ... ?
B: ...
3a Listen, say and trace
Aa Bb Cc Dd Ee Ff Gg
3b Listen and number the following letters. Then circle the letters with the same sound as Bb.
Aa Bb Cc Dd Ee Ff Gg
3c Match the big letters with the small ones. Then write them on the lines.
"""
lesson = ""
if lesson_file and Path(lesson_file).exists():
async with aiofiles.open(lesson_file, mode="r", encoding="utf-8") as reader:
lesson = await reader.read()
logger.info(f"Course content: {lesson}")
if not lesson:
logger.info("No course content provided, using the demo course.")
lesson = demo_lesson
yaml_filename = kwargs["config"]
kwargs["lesson"] = lesson
with open(yaml_filename, "r") as reader:
configs = yaml.safe_load(reader)
startup_config = ProjectConfig(**configs)
company = SoftwareCompany()
roles = UMLMetaRoleFactory.create_roles(role_configs=startup_config.roles,
options=company.options,
cost_manager=company.cost_manager,
**kwargs)
company.hire(roles)
company.invest(startup_config.startup.investment)
company.start_project(lesson, role=startup_config.startup.role,
cause_by=MetaAction.get_action_type(startup_config.startup.requirement))
await company.run(n_round=startup_config.startup.n_round)
def main(idea: str, investment: float = 3.0, n_round: int = 5, *args, **kwargs):
"""
We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities.
:param idea: lesson filename.
:param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company.
:param n_round: Reserved.
:param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3`
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
:return:
"""
asyncio.run(startup(idea, investment, n_round, *args, **kwargs))
if __name__ == '__main__':
"""
Formats:
```
python write_teaching_plan.py lesson_filename --teaching_language=<the language you are teaching> --language=<your native language>
```
If `lesson_filename` is not available, a demo lesson content will be used.
"""
fire.Fire(main)

View file

@ -4,9 +4,12 @@
@Time : 2023/5/6 14:13
@Author : alexanderwu
@File : llm_hello_world.py
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
"""
import asyncio
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parent.parent))
from metagpt.llm import LLM, Claude
from metagpt.logs import logger

View file

@ -4,10 +4,13 @@
@Time : 2023/5/7 18:32
@Author : alexanderwu
@File : search_google.py
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
"""
import asyncio
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parent.parent))
from metagpt.roles import Searcher

View file

@ -2,9 +2,12 @@
# -*- coding: utf-8 -*-
"""
@File : search_kb.py
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
"""
import asyncio
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parent.parent))
from metagpt.const import DATA_PATH
from metagpt.document_store import FaissStore
from metagpt.logs import logger

View file

@ -1,5 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Modified By: mashenquan, 2023-8-9, fix-bug: cannot find metagpt module.
"""
import asyncio
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parent.parent))
from metagpt.roles import Searcher
from metagpt.tools import SearchEngineType

View file

@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023-07-27
@Author : mashenquan
@File : write_teaching_plan.py
@Desc: Write teaching plan demo
"""
import asyncio
from pathlib import Path
import sys
sys.path.append(str(Path(__file__).resolve().parent.parent))
import aiofiles
import fire
from metagpt.logs import logger
from metagpt.actions.write_teaching_plan import TeachingPlanRequirement
from metagpt.roles.teacher import Teacher
from metagpt.software_company import SoftwareCompany
async def startup(lesson_file: str, investment: float = 3.0, n_round: int = 1, *args, **kwargs):
"""Run a startup. Be a teacher in education industry."""
demo_lesson = """
UNIT 1 Making New Friends
TOPIC 1 Welcome to China!
Section A
1a Listen and number the following names.
Jane Mari Kangkang Michael
Look, listen and understand. Then practice the conversation.
Work in groups. Introduce yourself using
I m ... Then practice 1a
with your own hometown or the following places.
1b Listen and number the following names
Jane Michael Maria Kangkang
1c Work in groups. Introduce yourself using I m ... Then practice 1a with your own hometown or the following places.
China the USA the UK Hong Kong Beijing
2a Look, listen and understand. Then practice the conversation
Hello!
Hello!
Hello!
Hello! Are you Maria?
No, Im not. Im Jane.
Oh, nice to meet you, Jane
Nice to meet you, too.
Hi, Maria!
Hi, Kangkang!
Welcome to China!
Thanks.
2b Work in groups. Make up a conversation with your own name and the
following structures.
A: Hello! / Good morning! / Hi! Im ... Are you ... ?
B: ...
3a Listen, say and trace
Aa Bb Cc Dd Ee Ff Gg
3b Listen and number the following letters. Then circle the letters with the same sound as Bb.
Aa Bb Cc Dd Ee Ff Gg
3c Match the big letters with the small ones. Then write them on the lines.
"""
lesson = ""
if lesson_file and Path(lesson_file).exists():
async with aiofiles.open(lesson_file, mode="r", encoding="utf-8") as reader:
lesson = await reader.read()
logger.info(f"Course content: {lesson}")
if not lesson:
logger.info("No course content provided, using the demo course.")
lesson = demo_lesson
company = SoftwareCompany()
company.hire([Teacher(options=company.options, cost_manager=company.cost_manager, *args, **kwargs)])
company.invest(investment)
company.start_project(lesson, role="Teacher", cause_by=TeachingPlanRequirement)
await company.run(n_round=1)
def main(idea: str, investment: float = 3.0, n_round: int = 5, *args, **kwargs):
"""
We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities.
:param idea: lesson filename.
:param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company.
:param n_round: Reserved.
:param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3`
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
:return:
"""
asyncio.run(startup(idea, investment, n_round, *args, **kwargs))
if __name__ == '__main__':
"""
Formats:
```
python write_teaching_plan.py lesson_filename --teaching_language=<the language you are teaching> --language=<your native language>
```
If `lesson_filename` is not available, a demo lesson content will be used.
"""
fire.Fire(main)

View file

@ -4,6 +4,7 @@
@Time : 2023/5/11 19:26
@Author : alexanderwu
@File : design_api.py
@Modified By: mashenquan, 2023-8-9, align `run` parameters with the parent :class:`Action` class.
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
"""
import shutil
@ -136,7 +137,7 @@ class WriteDesign(Action):
self._save_prd(docs_path, resources_path, context[-1].content)
self._save_system_design(docs_path, resources_path, content)
async def run(self, context):
async def run(self, context, **kwargs):
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)

View file

@ -0,0 +1,64 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/7
@Author : mashenquan
@File : meta_action.py
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
make these symbols configurable and standardized, making the process of building flows more convenient.
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
This file defines a meta action capable of generating arbitrary actions at runtime based on a
configuration file.
"""
from typing import Type
from metagpt.actions import Action
from metagpt.logs import logger
from metagpt.roles.uml_meta_role_options import MetaActionOptions
from metagpt.schema import Message
class MetaAction(Action):
def __init__(self, options, action_options: MetaActionOptions, llm=None, **kwargs):
super(MetaAction, self).__init__(options=options,
name=action_options.name,
context=kwargs.get("context"),
llm=llm)
self.prompt = action_options.format_prompt(**kwargs)
self.action_options = action_options
self.kwargs = kwargs
def __str__(self):
"""Return `topic` value when str()"""
return self.action_options.topic
def __repr__(self):
"""Show `topic` value when debug"""
return self.action_options.topic
async def run(self, messages, *args, **kwargs):
if len(messages) < 1 or not isinstance(messages[0], Message):
raise ValueError("Invalid args, a tuple of List[Message] is expected")
logger.debug(self.prompt)
rsp = await self._aask(prompt=self.prompt)
logger.debug(rsp)
self._set_result(rsp)
return self.rsp
def _set_result(self, rsp):
if self.action_options.rsp_begin_tag and self.action_options.rsp_begin_tag in rsp:
ix = rsp.index(self.action_options.rsp_begin_tag)
rsp = rsp[ix + len(self.action_options.rsp_begin_tag):]
if self.action_options.rsp_end_tag and self.action_options.rsp_end_tag in rsp:
ix = rsp.index(self.action_options.rsp_end_tag)
rsp = rsp[0:ix]
self.rsp = rsp.strip()
@staticmethod
def get_action_type(topic: str):
"""Create a runtime :class:`Action` subclass"""
action_type: Type["Action"] = type(topic, (Action,), {"name": topic})
return action_type

View file

@ -4,6 +4,7 @@
@Time : 2023/5/11 19:12
@Author : alexanderwu
@File : project_management.py
@Modified By: mashenquan, 2023-8-9, align `run` parameters with the parent :class:`Action` class.
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
"""
from typing import List, Tuple
@ -116,7 +117,7 @@ class WriteTasks(Action):
requirements_path = WORKSPACE_ROOT / ws_name / 'requirements.txt'
requirements_path.write_text(rsp.instruct_content.dict().get("Required Python third-party packages").strip('"\n'))
async def run(self, context):
async def run(self, context, **kwargs):
prompt = PROMPT_TEMPLATE.format(context=context, format_example=FORMAT_EXAMPLE)
rsp = await self._aask_v1(prompt, "task", OUTPUT_MAPPING)
self._save(context, rsp)

View file

@ -0,0 +1,159 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/7/27
@Author : mashenquan
@File : write_teaching_plan.py
"""
from metagpt.logs import logger
from metagpt.actions import Action
from metagpt.schema import Message
class TeachingPlanRequirement(Action):
"""Teaching Plan Requirement without any implementation details"""
async def run(self, *args, **kwargs):
raise NotImplementedError
class WriteTeachingPlanPart(Action):
"""Write Teaching Plan Part"""
def __init__(self, options, name: str = "", context=None, llm=None, topic: str = "", language: str = "Chinese"):
"""
:param name: action name
:param context: context
:param llm: object of :class:`LLM`
:param topic: topic part of teaching plan
:param language: A human language, such as Chinese, English, French, etc.
"""
super().__init__(options, name, context, llm)
self.topic = topic
self.language = language
self.rsp = None
async def run(self, messages, *args, **kwargs):
if len(messages) < 1 or not isinstance(messages[0], Message):
raise ValueError("Invalid args, a tuple of List[Message] is expected")
statement_patterns = self.TOPIC_STATEMENTS.get(self.topic, [])
statements = []
from metagpt.roles import Role
for p in statement_patterns:
s = Role.format_value(p, kwargs)
statements.append(s)
formatter = self.PROMPT_TITLE_TEMPLATE if self.topic == self.COURSE_TITLE else self.PROMPT_TEMPLATE
prompt = formatter.format(formation=self.FORMATION,
role=self.prefix,
statements="\n".join(statements),
lesson=messages[0].content,
topic=self.topic,
language=self.language)
logger.debug(prompt)
rsp = await self._aask(prompt=prompt)
logger.debug(rsp)
self._set_result(rsp)
return self.rsp
def _set_result(self, rsp):
if self.DATA_BEGIN_TAG in rsp:
ix = rsp.index(self.DATA_BEGIN_TAG)
rsp = rsp[ix + len(self.DATA_BEGIN_TAG):]
if self.DATA_END_TAG in rsp:
ix = rsp.index(self.DATA_END_TAG)
rsp = rsp[0:ix]
self.rsp = rsp.strip()
if self.topic != self.COURSE_TITLE:
return
if '#' not in self.rsp or self.rsp.index('#') != 0:
self.rsp = "# " + self.rsp
def __str__(self):
"""Return `topic` value when str()"""
return self.topic
def __repr__(self):
"""Show `topic` value when debug"""
return self.topic
FORMATION = "\"Capacity and role\" defines the role you are currently playing;\n" \
"\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n" \
"\t\"Statement\" defines the work detail you need to complete at this stage;\n" \
"\t\"Answer options\" defines the format requirements for your responses;\n" \
"\t\"Constraint\" defines the conditions that your responses must comply with."
COURSE_TITLE = "Title"
TOPICS = [
COURSE_TITLE, "Teaching Hours", "Teaching Objectives", "Teaching Content",
"Teaching Methods and Strategies", "Learning Activities",
"Teaching Time Allocation", "Assessment and Feedback", "Teaching Summary and Improvement",
"Vocabulary Cloze", "Choice Questions", "Grammar Questions", "Translation Questions"
]
TOPIC_STATEMENTS = {
COURSE_TITLE: ["Statement: Find and return the title of the lesson only in markdown first-level header format, "
"without anything else."],
"Teaching Content": [
"Statement: \"Teaching Content\" must include vocabulary, analysis, and examples of various grammar "
"structures that appear in the textbook, as well as the listening materials and key points.",
"Statement: \"Teaching Content\" must include more examples."],
"Teaching Time Allocation": [
"Statement: \"Teaching Time Allocation\" must include how much time is allocated to each "
"part of the textbook content."],
"Teaching Methods and Strategies": [
"Statement: \"Teaching Methods and Strategies\" must include teaching focus, difficulties, materials, "
"procedures, in detail."
],
"Vocabulary Cloze": [
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
"create vocabulary cloze. The cloze should include 10 {language} questions with {teaching_language} "
"answers, and it should also include 10 {teaching_language} questions with {language} answers. "
"The key-related vocabulary and phrases in the textbook content must all be included in the exercises.",
],
"Grammar Questions": [
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
"create grammar questions. 10 questions."],
"Choice Questions": [
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
"create choice questions. 10 questions."],
"Translation Questions": [
"Statement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", "
"create translation questions. The translation should include 10 {language} questions with "
"{teaching_language} answers, and it should also include 10 {teaching_language} questions with "
"{language} answers."
]
}
# Teaching plan title
PROMPT_TITLE_TEMPLATE = "Do not refer to the context of the previous conversation records, " \
"start the conversation anew.\n\n" \
"Formation: {formation}\n\n" \
"{statements}\n" \
"Constraint: Writing in {language}.\n" \
"Answer options: Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" " \
"and \"[TEACHING_PLAN_END]\" tags.\n" \
"[LESSON_BEGIN]\n" \
"{lesson}\n" \
"[LESSON_END]"
# Teaching plan parts:
PROMPT_TEMPLATE = "Do not refer to the context of the previous conversation records, " \
"start the conversation anew.\n\n" \
"Formation: {formation}\n\n" \
"Capacity and role: {role}\n" \
"Statement: Write the \"{topic}\" part of teaching plan, " \
"WITHOUT ANY content unrelated to \"{topic}\"!!\n" \
"{statements}\n" \
"Answer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" " \
"and \"[TEACHING_PLAN_END]\" tags.\n" \
"Answer options: Using proper markdown format from second-level header format.\n" \
"Constraint: Writing in {language}.\n" \
"[LESSON_BEGIN]\n" \
"{lesson}\n" \
"[LESSON_END]"
DATA_BEGIN_TAG = "[TEACHING_PLAN_BEGIN]"
DATA_END_TAG = "[TEACHING_PLAN_END]"

View file

@ -4,6 +4,8 @@
@Time : 2023/5/20 12:15
@Author : alexanderwu
@File : memory.py
@Modified By: mashenquan, 2023-8-7. Modified get_by_actions() to support for dynamically generated Action classes
at runtime.
"""
from collections import defaultdict
from typing import Iterable, Type
@ -80,8 +82,12 @@ class Memory:
def get_by_actions(self, actions: Iterable[Type[Action]]) -> list[Message]:
"""Return all messages triggered by specified Actions"""
rsp = []
# Using the `type(obj).__name__` approach to support the runtime creation of requirement classes.
# See `MetaAction.get_action_type()` for more.
class_names = {type(k).__name__: k for k in self.index.keys()}
for action in actions:
if action not in self.index:
if type(action).__name__ not in class_names:
continue
rsp += self.index[action]
key = class_names[type(action).__name__]
rsp += self.index[key]
return rsp

View file

@ -8,8 +8,9 @@
"""
import asyncio
import time
from typing import NamedTuple, Dict
from typing import NamedTuple
import traceback
import openai
from openai.error import APIConnectionError
from pydantic import BaseModel
@ -150,7 +151,15 @@ class OpenAIGPTAPI(BaseGPTAPI, RateLimiter):
self.rpm = int(self._options.get("RPM", 10))
async def _achat_completion_stream(self, messages: list[dict]) -> str:
response = await openai.ChatCompletion.acreate(**self._cons_kwargs(messages), stream=True)
try:
response = await openai.ChatCompletion.acreate(
**self._cons_kwargs(messages),
stream=True
)
except Exception as e:
error_str = traceback.format_exc()
logger.error(f"Exception:{e}, stack:{error_str}")
raise e
# create variables to collect the stream of chunks
collected_chunks = []

View file

@ -0,0 +1,130 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/7
@Author : mashenquan
@File : fork_meta_role.py
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
make these symbols configurable and standardized, making the process of building flows more convenient.
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
This file defines a `fork` style meta role capable of generating arbitrary roles at runtime based on a
configuration file.
"""
import re
import aiofiles
from metagpt.actions.meta_action import MetaAction
from metagpt.const import WORKSPACE_ROOT
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.roles.uml_meta_role_options import MetaActionOptions, UMLMetaRoleOptions
from metagpt.schema import Message
class ForkMetaRole(Role):
"""A `fork` style meta role capable of generating arbitrary roles at runtime based on a configuration file"""
def __init__(self, options, cost_manager, role_options, **kwargs):
"""Initialize a `fork` style meta role
:param options: System configuration
:param cost_manager: Cost manager
:param role_options: pattern yaml file data
:param args: Parameters passed in format: `python your_script.py arg1 arg2 arg3`
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
"""
opts = UMLMetaRoleOptions(**role_options)
global_variables = {
"name": Role.format_value(opts.name, kwargs),
"profile": Role.format_value(opts.profile, kwargs),
"goal": Role.format_value(opts.goal, kwargs),
"constraints": Role.format_value(opts.constraints, kwargs),
"desc": Role.format_value(opts.desc, kwargs),
"role": Role.format_value(opts.role, kwargs)
}
for k, v in kwargs.items():
if k not in global_variables:
global_variables[k] = v
super(ForkMetaRole, self).__init__(
options=options,
cost_manager=cost_manager,
name=global_variables["name"],
profile=global_variables["profile"],
goal=global_variables["goal"],
constraints=global_variables["constraints"],
desc=global_variables["desc"],
**kwargs
)
actions = []
for m in opts.actions:
for k, v in m.items():
v = Role.format_value(v, kwargs)
m[k] = v
for k, v in global_variables.items():
if k not in m:
m[k] = v
o = MetaActionOptions(**m)
o.set_default_template(opts.templates[o.template_ix])
act = MetaAction(options=options, action_options=o, llm=self._llm, **m)
actions.append(act)
self._init_actions(actions)
requirement_types = set()
for v in opts.requirement:
requirement_types.add(MetaAction.get_action_type(v))
self._watch(requirement_types)
async def _think(self) -> None:
"""Everything will be done part by part."""
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 _react(self) -> Message:
ret = Message(content="")
while True:
await self._think()
if self._rc.todo is None:
break
logger.debug(f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}")
msg = await self._act()
if ret.content != '':
ret.content += "\n\n\n"
ret.content += msg.content
logger.info(ret.content)
await self.save(ret.content)
return ret
async def save(self, content):
"""Save teaching plan"""
output_filename = self.options.get("output_filename")
if not output_filename:
return
filename = ForkMetaRole.new_file_name(output_filename)
pathname = WORKSPACE_ROOT / "teaching_plan"
pathname.mkdir(exist_ok=True)
pathname = pathname / filename
try:
async with aiofiles.open(str(pathname), mode='w', encoding='utf-8') as writer:
await writer.write(content)
except Exception as e:
logger.error(f'Save failed{e}')
logger.info(f"Save to:{pathname}")
@staticmethod
def new_file_name(lesson_title, ext=".md"):
"""Create a related file name based on `lesson_title` and `ext`."""
# Define the special characters that need to be replaced.
illegal_chars = r'[#@$%!*&\\/:*?"<>|\n\t \']'
# Replace the special characters with underscores.
filename = re.sub(illegal_chars, '_', lesson_title) + ext
return re.sub(r'_+', '_', filename)

View file

@ -4,6 +4,7 @@
@Time : 2023/5/11 14:42
@Author : alexanderwu
@File : role.py
@Modified By: mashenquan, 2023-8-7, :class:`Role` + properties.
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation;
Change cost control from global to company level.
"""
@ -47,7 +48,7 @@ ROLE_TEMPLATE = """Your response should be based on the previous conversation hi
class RoleSetting(BaseModel):
"""角色设定"""
"""Role properties"""
name: str
profile: str
goal: str
@ -62,7 +63,7 @@ class RoleSetting(BaseModel):
class RoleContext(BaseModel):
"""角色运行时上下文"""
"""Runtime role context"""
env: 'Environment' = Field(default=None)
memory: Memory = Field(default_factory=Memory)
long_term_memory: LongTermMemory = Field(default_factory=LongTermMemory)
@ -82,7 +83,7 @@ class RoleContext(BaseModel):
@property
def important_memory(self) -> list[Message]:
"""获得关注动作对应的信息"""
"""Retrieve information corresponding to the attention action."""
return self.memory.get_by_actions(self.watch)
@property
@ -91,17 +92,25 @@ class RoleContext(BaseModel):
class Role:
"""角色/代理"""
"""Role/Proxy"""
def __init__(self, options, cost_manager, name="", profile="", goal="", constraints="", desc="", *args, **kwargs):
self._options = Role.supply_options(options=kwargs, default_options=options)
name = Role.format_value(name, self._options)
profile = Role.format_value(profile, self._options)
goal = Role.format_value(goal, self._options)
constraints = Role.format_value(constraints, self._options)
desc = Role.format_value(desc, self._options)
def __init__(self, options, cost_manager, name="", profile="", goal="", constraints="", desc=""):
self._options = options if options else {}
self._cost_manager = cost_manager
self._llm = LLM(options=self._options, cost_manager=cost_manager)
self._setting = RoleSetting(name=name, profile=profile, goal=goal, constraints=constraints, desc=desc)
self._states = []
self._actions = []
self._role_id = str(self._setting)
self._rc = RoleContext(options=options)
self._rc = RoleContext(options=self._options)
def _reset(self):
self._states = []
@ -139,6 +148,31 @@ class Role:
"""获取角色描述(职位)"""
return self._setting.profile
@property
def name(self):
"""Return role `name`, read only"""
return self._setting.name
@property
def desc(self):
"""Return role `desc`, read only"""
return self._setting.desc
@property
def goal(self):
"""Return role `goal`, read only"""
return self._setting.goal
@property
def constraints(self):
"""Return role `constraints`, read only"""
return self._setting.constraints
@property
def action_count(self):
"""Return number of action"""
return len(self._actions)
@property
def options(self):
return self._options
@ -175,7 +209,8 @@ class Role:
# history=self.history)
logger.info(f"{self._setting}: ready to {self._rc.todo}")
response = await self._rc.todo.run(self._rc.important_memory)
requirement = self._rc.important_memory
response = await self._rc.todo.run(requirement, **self._options)
# logger.info(response)
if isinstance(response, ActionOutput):
msg = Message(content=response.content, instruct_content=response.instruct_content,
@ -192,9 +227,9 @@ class Role:
if not self._rc.env:
return 0
env_msgs = self._rc.env.memory.get()
observed = self._rc.env.memory.get_by_actions(self._rc.watch)
self._rc.news = self._rc.memory.remember(observed) # remember recent exact or similar memories
for i in env_msgs:
@ -251,3 +286,30 @@ class Role:
# 将回复发布到环境,等待下一个订阅者处理
self._publish_message(rsp)
return rsp
@staticmethod
def supply_options(options, default_options=None):
"""Supply missing options"""
ret = default_options.copy() if default_options else {}
if not options:
return ret
ret.update(options)
return ret
@staticmethod
def format_value(value, opts, default_opts=None):
"""Fill parameters inside `value` with `options`."""
if not isinstance(value, str):
return value
if "{" not in value:
return value
merged_opts = Role.supply_options(opts, default_opts)
try:
return value.format(**merged_opts)
except KeyError as e:
logger.warning(f"Parameter is missing:{e}")
for k, v in merged_opts.items():
value = value.replace("{" + f"{k}" + "}", str(v))
return value

97
metagpt/roles/teacher.py Normal file
View file

@ -0,0 +1,97 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/7/27
@Author : mashenquan
@File : teacher.py
"""
import aiofiles
from metagpt.actions.write_teaching_plan import WriteTeachingPlanPart, TeachingPlanRequirement
from metagpt.const import WORKSPACE_ROOT
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.logs import logger
import re
class Teacher(Role):
"""Support configurable teacher roles,
with native and teaching languages being replaceable through configurations."""
def __init__(self, options, name='Lily', profile='{teaching_language} Teacher',
goal='writing a {language} teaching plan part by part',
constraints='writing in {language}', desc="", *args, **kwargs):
super().__init__(options=options, name=name, profile=profile, goal=goal, constraints=constraints, desc=desc, *args, **kwargs)
actions = []
for topic in WriteTeachingPlanPart.TOPICS:
act = WriteTeachingPlanPart(options=options, topic=topic, llm=self._llm)
actions.append(act)
self._init_actions(actions)
self._watch({TeachingPlanRequirement})
async def _think(self) -> None:
"""Everything will be done part by part."""
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 _react(self) -> Message:
ret = Message(content="")
while True:
await self._think()
if self._rc.todo is None:
break
logger.debug(f"{self._setting}: {self._rc.state=}, will do {self._rc.todo}")
msg = await self._act()
if ret.content != '':
ret.content += "\n\n\n"
ret.content += msg.content
logger.info(ret.content)
await self.save(ret.content)
return ret
async def save(self, content):
"""Save teaching plan"""
filename = Teacher.new_file_name(self.course_title)
pathname = WORKSPACE_ROOT / "teaching_plan"
pathname.mkdir(exist_ok=True)
pathname = pathname / filename
try:
async with aiofiles.open(str(pathname), mode='w', encoding='utf-8') as writer:
await writer.write(content)
except Exception as e:
logger.error(f'Save failed{e}')
logger.info(f"Save to:{pathname}")
@staticmethod
def new_file_name(lesson_title, ext=".md"):
"""Create a related file name based on `lesson_title` and `ext`."""
# Define the special characters that need to be replaced.
illegal_chars = r'[#@$%!*&\\/:*?"<>|\n\t \']'
# Replace the special characters with underscores.
filename = re.sub(illegal_chars, '_', lesson_title) + ext
return re.sub(r'_+', '_', filename)
@property
def course_title(self):
"""Return course title of teaching plan"""
default_title = "teaching_plan"
for act in self._actions:
if act.topic != WriteTeachingPlanPart.COURSE_TITLE:
continue
if act.rsp is None:
return default_title
title = act.rsp.lstrip("# \n")
if '\n' in title:
ix = title.index('\n')
title = title[0: ix]
return title
return default_title

View file

@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/7
@Author : mashenquan
@File : uml_meta_role_factory.py
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
make these symbols configurable and standardized, making the process of building flows more convenient.
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
"""
from metagpt.roles.fork_meta_role import ForkMetaRole
from metagpt.roles.uml_meta_role_options import UMLMetaRoleOptions
class UMLMetaRoleFactory:
"""Factory of UML activity role classes"""
@classmethod
def create_roles(cls, role_configs, **kwargs):
"""Generate the flow of the project based on the configuration in the format of config/pattern/template.yaml.
:param role_configs: `roles` field of template.yaml
:param kwargs: Parameters passed in format: `python your_script.py --param1=value1 --param2=value2`
"""
roles = []
for m in role_configs:
opt = UMLMetaRoleOptions(**m)
constructor = cls.CONSTRUCTORS.get(opt.role_type)
if constructor is None:
raise NotImplementedError(
f"{opt.role_type} is not implemented"
)
r = constructor(role_options=m, **kwargs)
roles.append(r)
return roles
CONSTRUCTORS = {
"fork": ForkMetaRole,
# TODO: add more activity node constructor here..
}

View file

@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/7
@Author : mashenquan
@File : uml_meta_role_options.py
@Desc : I am attempting to incorporate certain symbol concepts from UML into MetaGPT, enabling it to have the
ability to freely construct flows through symbol concatenation. Simultaneously, I am also striving to
make these symbols configurable and standardized, making the process of building flows more convenient.
For more about `fork` node in activity diagrams, see: `https://www.uml-diagrams.org/activity-diagrams.html`
"""
from typing import List, Dict
from pydantic import BaseModel
# `startup` field of config/pattern/template.yaml
class StartupConfig(BaseModel):
requirement: str
role: str
investment: float = 3.0
n_round: int = 3
# config/pattern/template.yaml
class ProjectConfig(BaseModel):
startup: StartupConfig
roles: List[Dict]
# element of `actions` field of config/pattern/template.yaml
class MetaActionOptions(BaseModel):
topic: str
name: str = ""
language: str = "Chinese"
template_ix: int = 0
statements: List[str] = []
template: str = ""
rsp_begin_tag: str = ""
rsp_end_tag: str = ""
def set_default_template(self, v):
if not self.template:
self.template = v
def format_prompt(self, **kwargs):
statements = "\n".join(self.statements)
opts = kwargs.copy()
opts["statements"] = statements
from metagpt.roles import Role
prompt = Role.format_value(self.template, opts)
return prompt
# element of `roles` field of config/pattern/template.yaml
class UMLMetaRoleOptions(BaseModel):
role_type: str
name: str = ""
profile: str = ""
goal: str = ""
role: str = ""
constraints: str = ""
desc: str = ""
templates: List[str] = []
output_filename: str = ""
actions: List
requirement: List

View file

@ -4,6 +4,7 @@
@Time : 2023/5/12 00:30
@Author : alexanderwu
@File : software_company.py
@Modified By: mashenquan, 2023-07-27, Add `role` & `cause_by` parameters to `start_project()`.
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation;
Change cost control from global to company level.
"""
@ -49,10 +50,10 @@ class SoftwareCompany(BaseModel):
if self.total_cost > self.max_budget:
raise NoMoneyException(self.total_cost, f'Insufficient funds: {self.max_budget}')
def start_project(self, idea):
def start_project(self, idea, role="BOSS", cause_by=BossRequirement):
"""Start a project from publishing boss requirement."""
self.idea = idea
self.environment.publish_message(Message(role="BOSS", content=idea, cause_by=BossRequirement))
self.environment.publish_message(Message(role=role, content=idea, cause_by=cause_by))
def _save(self):
logger.info(self.json())

41
requirements-test.txt Normal file
View file

@ -0,0 +1,41 @@
aiohttp==3.8.4
azure-cognitiveservices-speech==1.30.0
channels==4.0.0
chromadb==0.3.22
# Django==4.1.5
# docx==0.2.4
duckduckgo_search==2.9.4
#faiss==1.5.3
faiss_cpu==1.7.4
fire==0.4.0
# godot==0.1.1
# google_api_python_client==2.93.0
langchain==0.0.231
loguru==0.6.0
meilisearch==0.21.0
numpy==1.24.3
openai==0.27.8
openpyxl
pandas==1.4.1
pydantic==1.10.7
#pygame==2.1.3
pymilvus==2.2.8
pytest==7.2.2
python_docx==0.8.11
PyYAML==6.0
# sentence_transformers==2.2.2
setuptools==65.6.3
tenacity==8.2.2
tiktoken==0.3.3
tqdm==4.64.0
#unstructured[local-inference]
playwright
selenium>4
webdriver_manager<3.9
anthropic==0.3.6
typing-inspect==0.8.0
typing_extensions==4.5.0
bs4
aiofiles
pytest
pytest-asyncio

View file

@ -35,7 +35,9 @@ tqdm==4.64.0
anthropic==0.3.6
typing-inspect==0.8.0
typing_extensions==4.5.0
aiofiles
libcst==1.0.1
qdrant-client==1.4.0
connexion[swagger-ui]
aiohttp_jinja2
aiohttp_jinja2

View file

@ -0,0 +1,51 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/8
@Author : mashenquan
@File : test_meta_action.py
"""
from typing import Dict
from pydantic import BaseModel
from metagpt.actions.meta_action import MetaAction
from metagpt.roles.uml_meta_role_options import MetaActionOptions
def test_meta_action_create():
class Inputs(BaseModel):
options: Dict
kwargs: Dict
expect_class_name: str
expect_prompt: str
inputs = [
{
"options": {
"topic": "TOPIC_A",
"name": "A",
"language": "XX",
"template_ix": 0,
"statements": ["Statement A", "Statement B"],
"template": "{statements}",
"rsp_begin_tag": "",
"rsp_end_tag": ""
},
"kwargs": {},
"expect_class_name": "TOPIC_A",
"expect_prompt": "\n".join(["Statement A", "Statement B"]),
}
]
for i in inputs:
seed = Inputs(**i)
opt = MetaActionOptions(**seed.options)
act = MetaAction(opt, **seed.kwargs)
assert seed.expect_prompt == act.prompt
t = MetaAction.get_action_type(seed.expect_class_name)
assert t.__name__ == seed.expect_class_name
if __name__ == '__main__':
test_meta_action_create()

View file

@ -4,7 +4,7 @@
#
from tests.metagpt.roles.ui_role import UIDesign
llm_resp= '''
llm_resp = '''
# UI Design Description
```The user interface for the snake game will be designed in a way that is simple, clean, and intuitive. The main elements of the game such as the game grid, snake, food, score, and game over message will be clearly defined and easy to understand. The game grid will be centered on the screen with the score displayed at the top. The game controls will be intuitive and easy to use. The design will be modern and minimalist with a pleasing color scheme.```
@ -100,6 +100,7 @@ body {
font-size: 3em;
'''
def test_ui_design_parse_css():
ui_design_work = UIDesign(name="UI design action")
@ -161,7 +162,7 @@ def test_ui_design_parse_css():
transform: translate(-50%, -50%);
font-size: 3em;
'''
assert ui_design_work.parse_css_code(context=llm_resp)==css
assert ui_design_work.parse_css_code(context=llm_resp) == css
def test_ui_design_parse_html():
@ -185,7 +186,4 @@ def test_ui_design_parse_html():
</body>
</html>
'''
assert ui_design_work.parse_css_code(context=llm_resp)==html
assert ui_design_work.parse_css_code(context=llm_resp) == html

View file

@ -4,6 +4,7 @@
@Time : 2023/5/11 17:45
@Author : alexanderwu
@File : test_write_code.py
@Modified By: mashenquan, 2023-8-1, fix-bug: `filename` of `write_code.run()` is missing.
@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation.
"""
import pytest
@ -22,8 +23,7 @@ async def test_write_code():
cost_manager = CostManager(**conf.runtime_options)
llm = LLM(options=conf.runtime_options, cost_manager=cost_manager)
write_code = WriteCode(options=conf.runtime_options, name="write_code", llm=llm)
code = await write_code.run(api_design, "filename")
code = await write_code.run(context=api_design, filename="test")
logger.info(code)
# 我们不能精确地预测生成的代码,但我们可以检查某些关键字

View file

@ -0,0 +1,67 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/7/28 17:25
@Author : mashenquan
@File : test_write_teaching_plan.py
"""
import asyncio
from typing import Optional
from pydantic import BaseModel
from langchain.llms.base import LLM
from metagpt.actions.write_teaching_plan import WriteTeachingPlanPart
from metagpt.config import Config
from metagpt.schema import Message
class MockWriteTeachingPlanPart(WriteTeachingPlanPart):
def __init__(self, options, name: str = '', context=None, llm: LLM = None, topic="", language="Chinese"):
super().__init__(options, name, context, llm, topic, language)
async def _aask(self, prompt: str, system_msgs: Optional[list[str]] = None) -> str:
return f"{WriteTeachingPlanPart.DATA_BEGIN_TAG}\nprompt\n{WriteTeachingPlanPart.DATA_END_TAG}"
async def mock_write_teaching_plan_part():
class Inputs(BaseModel):
input: str
name: str
topic: str
language: str
inputs = [
{
"input": "AABBCC",
"name": "A",
"topic": WriteTeachingPlanPart.COURSE_TITLE,
"language": "C"
},
{
"input": "DDEEFFF",
"name": "A1",
"topic": "B1",
"language": "C1"
}
]
for i in inputs:
seed = Inputs(**i)
options = Config().runtime_options
act = MockWriteTeachingPlanPart(options=options, name=seed.name, topic=seed.topic, language=seed.language)
await act.run([Message(content="")])
assert act.topic == seed.topic
assert str(act) == seed.topic
assert act.name == seed.name
assert act.rsp == "# prompt" if seed.topic == WriteTeachingPlanPart.COURSE_TITLE else "prompt"
def test_suite():
loop = asyncio.get_event_loop()
task = loop.create_task(mock_write_teaching_plan_part())
loop.run_until_complete(task)
if __name__ == '__main__':
test_suite()

View file

@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/8
@Author : mashenquan
@File : test_fork_meta_role.py
"""
from typing import Dict
from pydantic import BaseModel
from metagpt.config import Config
from metagpt.provider.openai_api import CostManager
from metagpt.roles.fork_meta_role import ForkMetaRole
def test_creat_role():
class Inputs(BaseModel):
role: Dict
action_count: int
inputs = [
{
"role": {
"role_type": "fork",
"name": "Lily",
"profile": "{teaching_language} Teacher",
"goal": "writing a {language} teaching plan part by part",
"constraints": "writing in {language}",
"role": "You are a {teaching_language} Teacher, named Lily, your goal is writing a {"
"teaching_language} teaching plan part by part, and the constraint is writing in {language}.",
"desc": "",
"output_filename": "teaching_plan_demo.md",
"requirement": ["TeachingPlanRequirement"],
"templates": [
"Do not refer to the context of the previous conversation records, start the conversation "
"anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"["
"LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" "
"defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the "
"format requirements for your responses;\n\t\"Constraint\" defines the conditions that your "
"responses must comply with.\n\n{statements}\nConstraint: Writing in {language}.\nAnswer options: "
"Encloses the lesson title with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\n["
"LESSON_BEGIN]\n{lesson}\n[LESSON_END]",
"Do not refer to the context of the previous conversation records, start the conversation "
"anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"["
"LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" "
"defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the "
"format requirements for your responses;\n\t\"Constraint\" defines the conditions that your "
"responses must comply with.\n\nCapacity and role: {role}\nStatement: Write the \"{topic}\" part "
"of teaching plan, WITHOUT ANY content unrelated to \"{topic}\"!!\n{statements}\nAnswer options: "
"Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" "
"tags.\nAnswer options: Using proper markdown format from second-level header "
"format.\nConstraint: Writing in {language}.\n[LESSON_BEGIN]\n{lesson}\n[LESSON_END] "
],
"actions": [
{
"name": "",
"topic": "Title",
"language": "Chinese",
"statements": [
"Statement: Find and return the title of the lesson only with \"# \" prefixed, without "
"anything else."],
"template_ix": 0},
{
"name": "",
"topic": "Teaching Hours",
"language": "Chinese",
"statements": [],
"template_ix": 1,
"rsp_begin_tag": "[TEACHING_PLAN_BEGIN]",
"rsp_end_tag": "[TEACHING_PLAN_END]"}
]
},
"action_count": 2
}
]
for i in inputs:
seed = Inputs(**i)
kwargs = {
"teaching_language": "AA",
"language": "BB"
}
runtime_options = Config().runtime_options
cost_manager = CostManager(options=runtime_options)
role = ForkMetaRole(runtime_options=runtime_options, cost_manager=cost_manager, role_options=seed.role, **kwargs)
assert role.action_count == 2
assert "{" not in role.profile
assert "{" not in role.goal
assert "{" not in role.constraints
if __name__ == '__main__':
test_creat_role()

View file

@ -0,0 +1,101 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/7/27 13:25
@Author : mashenquan
@File : test_teacher.py
"""
from typing import Dict, Optional
from pydantic import BaseModel
from metagpt.config import Config
from metagpt.provider.openai_api import CostManager
from metagpt.roles.teacher import Teacher
def test_init():
class Inputs(BaseModel):
name: str
profile: str
goal: str
constraints: str
desc: str
kwargs: Optional[Dict] = None
expect_name: str
expect_profile: str
expect_goal: str
expect_constraints: str
expect_desc: str
inputs = [
{
"name": "Lily{language}",
"expect_name": "LilyCN",
"profile": "X {teaching_language}",
"expect_profile": "X EN",
"goal": "Do {something_big}, {language}",
"expect_goal": "Do sleep, CN",
"constraints": "Do in {key1}, {language}",
"expect_constraints": "Do in HaHa, CN",
"kwargs": {"language": "CN", "key1": "HaHa", "something_big": "sleep", "teaching_language": "EN"},
"desc": "aaa{language}",
"expect_desc": "aaaCN"
},
{
"name": "Lily{language}",
"expect_name": "Lily{language}",
"profile": "X {teaching_language}",
"expect_profile": "X {teaching_language}",
"goal": "Do {something_big}, {language}",
"expect_goal": "Do {something_big}, {language}",
"constraints": "Do in {key1}, {language}",
"expect_constraints": "Do in {key1}, {language}",
"kwargs": {},
"desc": "aaa{language}",
"expect_desc": "aaa{language}"
},
]
for i in inputs:
seed = Inputs(**i)
options = Config().runtime_options
cost_manager = CostManager(options=options)
teacher = Teacher(options=options, cost_manager=cost_manager, name=seed.name, profile=seed.profile,
goal=seed.goal, constraints=seed.constraints,
desc=seed.desc, **seed.kwargs)
assert teacher.name == seed.expect_name
assert teacher.desc == seed.expect_desc
assert teacher.profile == seed.expect_profile
assert teacher.goal == seed.expect_goal
assert teacher.constraints == seed.expect_constraints
assert teacher.course_title == "teaching_plan"
def test_new_file_name():
class Inputs(BaseModel):
lesson_title: str
ext: str
expect: str
inputs = [
{
"lesson_title": "# @344\n12",
"ext": ".md",
"expect": "_344_12.md"
},
{
"lesson_title": "1#@$%!*&\\/:*?\"<>|\n\t \'1",
"ext": ".cc",
"expect": "1_1.cc"
}
]
for i in inputs:
seed = Inputs(**i)
result = Teacher.new_file_name(seed.lesson_title, seed.ext)
assert result == seed.expect
if __name__ == '__main__':
test_init()
test_new_file_name()

View file

@ -0,0 +1,61 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/8
@Author : mashenquan
@File : test_uml_meta_role_factory.py
"""
from typing import List, Dict
from pydantic import BaseModel
from metagpt.roles.uml_meta_role_factory import UMLMetaRoleFactory
def test_create_roles():
class Inputs(BaseModel):
roles: List
kwargs: Dict
inputs = [
{
"roles": [
{
"role_type": "fork",
"name": "Lily",
"profile": "{teaching_language} Teacher",
"goal": "writing a {language} teaching plan part by part",
"constraints": "writing in {language}",
"role": "You are a {teaching_language} Teacher, named Lily.",
"desc": "",
"output_filename": "teaching_plan_demo.md",
"requirement": ["TeachingPlanRequirement"],
"templates": ["Do 1 {statements}", "Do 2 {statements}"],
"actions": [
{
"name": "",
"topic": "Title",
"language": "Chinese",
"statements": ["statement 1", "statement 2"]}
],
"template_ix": 0
}
],
"kwargs": {
"teaching_language": "AA",
"language": "BB",
}
}
]
for i in inputs:
seed = Inputs(**i)
roles = UMLMetaRoleFactory.create_roles(seed.roles, **seed.kwargs)
assert len(roles) == 1
assert "{" not in roles[0].profile
assert "{" not in roles[0].goal
assert roles[0].action_count == 1
if __name__ == '__main__':
test_create_roles()

View file

@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/8/8
@Author : mashenquan
@File : test_uml_meta_role_options.py
"""
from typing import List
from pydantic import BaseModel
from metagpt.roles.uml_meta_role_options import MetaActionOptions
def test_set_default_template():
class Inputs(BaseModel):
statements: List
template: str
expect_prompt: str
inputs = [
{
"statements": ["Statement: 1", "Statement: 2"],
"template": "{statements}",
"expect_prompt": "Statement: 1\nStatement: 2"
}
]
for i in inputs:
seed = Inputs(**i)
opt = MetaActionOptions(topic="", statements=seed.statements)
assert opt.template == ""
opt.set_default_template(seed.template)
assert opt.template == seed.template
kwargs = {}
assert opt.format_prompt(**kwargs) == seed.expect_prompt
if __name__ == '__main__':
test_set_default_template()

View file

@ -4,23 +4,46 @@
@Time : 2023/7/1 22:50
@Author : alexanderwu
@File : test_azure_tts.py
@Modified By: mashenquan, 2023-8-9, add more text formatting options
@Modified By: mashenquan, 2023-8-17, move to `tools` folder.
"""
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).resolve().parent.parent.parent.parent)) # fix-bug: No module named 'metagpt'
from metagpt.const import WORKSPACE_ROOT
from metagpt.tools.azure_tts import AzureTTS
from metagpt.utils.common import initialize_environment
def test_azure_tts():
azure_tts = AzureTTS("azure_tts")
azure_tts.synthesize_speech(
"zh-CN",
"zh-CN-YunxiNeural",
"Boy",
"你好,我是卡卡",
"output.wav")
initialize_environment()
azure_tts = AzureTTS()
text = """
女儿看见父亲走了进来问道
<mstts:express-as role="YoungAdultFemale" style="calm">
您来的挺快的怎么过来的
</mstts:express-as>
父亲放下手提包
<mstts:express-as role="OlderAdultMale" style="calm">
Writing a binary file in Python is similar to writing a regular text file, but you'll work with bytes instead of strings.”
</mstts:express-as>
"""
path = WORKSPACE_ROOT / "tts"
path.mkdir(exist_ok=True, parents=True)
filename = path / "girl.wav"
result = azure_tts.synthesize_speech(
lang="zh-CN",
voice="zh-CN-XiaomoNeural",
text=text,
output_file=str(filename))
print(result)
# 运行需要先配置 SUBSCRIPTION_KEY
# TODO: 这里如果要检验还要额外加上对应的asr才能确保前后生成是接近一致的但现在还没有
if __name__ == '__main__':
test_azure_tts()