This commit is contained in:
zhouzinimg 2023-10-18 22:17:16 +08:00
parent 315fb65430
commit 041da760e0
147 changed files with 756 additions and 7644 deletions

View file

@ -6,14 +6,14 @@
@File : conftest.py
"""
import asyncio
import re
from unittest.mock import Mock
import pytest
from metagpt.logs import logger
from metagpt.provider.openai_api import OpenAIGPTAPI as GPTAPI
import asyncio
import re
class Context:

View file

@ -159,6 +159,7 @@ sequenceDiagram
The original requirements did not specify whether the game should have a save/load feature, multiplayer support, or any specific graphical user interface. More information on these aspects could help in further refining the product design and requirements.
"""
PROJECT_MANAGEMENT_SAMPLE = '''## Required Python third-party packages: Provided in requirements.txt format
```python
"pytest==6.2.5"
@ -216,6 +217,7 @@ The original requirements did not specify whether the game should have a save/lo
```
'''
WRITE_CODE_PROMPT_SAMPLE = """
你是一个工程师下面是背景信息与你的当前任务请为任务撰写代码
撰写的代码应该符合PEP8优雅模块化易于阅读与维护代码本身应该有__main__入口来防止桩函数
@ -373,6 +375,7 @@ if __name__ == '__main__':
print('No results found.')
"""
REFINED_CODE = '''
import requests

View file

@ -2,6 +2,7 @@ import pytest
from metagpt.actions.clone_function import CloneFunction, run_function_code
source_code = """
import pandas as pd
import ta
@ -36,10 +37,7 @@ def get_expected_res():
stock_data['SMA'] = ta.trend.sma_indicator(stock_data['Close'], window=6)
stock_data[['Date', 'Close', 'SMA']].head()
# 计算布林带
stock_data['bb_upper'], stock_data['bb_middle'], stock_data['bb_lower'] = ta.volatility.bollinger_hband_indicator(
stock_data['Close'], window=20), ta.volatility.bollinger_mavg(stock_data['Close'],
window=20), ta.volatility.bollinger_lband_indicator(
stock_data['Close'], window=20)
stock_data['bb_upper'], stock_data['bb_middle'], stock_data['bb_lower'] = ta.volatility.bollinger_hband_indicator(stock_data['Close'], window=20), ta.volatility.bollinger_mavg(stock_data['Close'], window=20), ta.volatility.bollinger_lband_indicator(stock_data['Close'], window=20)
stock_data[['Date', 'Close', 'bb_upper', 'bb_middle', 'bb_lower']].head()
return stock_data

View file

@ -144,12 +144,12 @@ Engineer
---
'''
@pytest.mark.asyncio
async def test_debug_error():
debug_error = DebugError("debug_error")
file_name, rewritten_code = await debug_error.run(context=EXAMPLE_MSG_CONTENT)
assert "class Player" in rewritten_code # rewrite the same class
assert "while self.score > 21" in rewritten_code # a key logic to rewrite to (original one is "if self.score > 12")
assert "class Player" in rewritten_code # rewrite the same class
assert "while self.score > 21" in rewritten_code # a key logic to rewrite to (original one is "if self.score > 12")

View file

@ -10,7 +10,6 @@ import pytest
from metagpt.actions.detail_mining import DetailMining
from metagpt.logs import logger
@pytest.mark.asyncio
async def test_detail_mining():
topic = "如何做一个生日蛋糕"
@ -18,6 +17,7 @@ async def test_detail_mining():
detail_mining = DetailMining("detail_mining")
rsp = await detail_mining.run(topic=topic, record=record)
logger.info(f"{rsp.content=}")
assert '##OUTPUT' in rsp.content
assert '蛋糕' in rsp.content

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,7 +100,6 @@ body {
font-size: 3em;
'''
def test_ui_design_parse_css():
ui_design_work = UIDesign(name="UI design action")
@ -162,7 +161,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():
@ -186,4 +185,7 @@ 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

@ -27,6 +27,7 @@ def add(a, b):
captured = capfd.readouterr()
print(f"输出内容: {captured.out}")
# @pytest.mark.asyncio
# async def test_write_code_review_directly():
# code = SEARCH_CODE_SAMPLE

View file

@ -16,8 +16,8 @@ def test_chroma_store():
# 使用 write 方法添加多个文档
document_store.write(["This is document1", "This is document2"],
[{"source": "google-docs"}, {"source": "notion"}],
["doc1", "doc2"])
[{"source": "google-docs"}, {"source": "notion"}],
["doc1", "doc2"])
# 使用 add 方法添加一个文档
document_store.add("This is document3", {"source": "notion"}, "doc3")

View file

@ -5,30 +5,27 @@
@Author : unkn-wn (Leon Yee)
@File : test_lancedb_store.py
"""
import random
import pytest
from metagpt.document_store.lancedb_store import LanceStore
import pytest
import random
@pytest
def test_lance_store():
# This simply establishes the connection to the database, so we can drop the table if it exists
store = LanceStore('test')
store.drop('test')
store.write(data=[[random.random() for _ in range(100)] for _ in range(2)],
metadatas=[{"source": "google-docs"}, {"source": "notion"}],
ids=["doc1", "doc2"])
metadatas=[{"source": "google-docs"}, {"source": "notion"}],
ids=["doc1", "doc2"])
store.add(data=[random.random() for _ in range(100)], metadata={"source": "notion"}, _id="doc3")
result = store.search([random.random() for _ in range(100)], n_results=3)
assert (len(result) == 3)
assert(len(result) == 3)
store.delete("doc2")
result = store.search([random.random() for _ in range(100)], n_results=3, where="source = 'notion'",
metric='cosine')
assert (len(result) == 1)
result = store.search([random.random() for _ in range(100)], n_results=3, where="source = 'notion'", metric='cosine')
assert(len(result) == 1)

View file

@ -2,11 +2,11 @@
# -*- coding: utf-8 -*-
# @Desc : unittest of `metagpt/memory/longterm_memory.py`
from metagpt.actions import BossRequirement
from metagpt.config import CONFIG
from metagpt.memory import LongTermMemory
from metagpt.roles.role import RoleContext
from metagpt.schema import Message
from metagpt.actions import BossRequirement
from metagpt.roles.role import RoleContext
from metagpt.memory import LongTermMemory
def test_ltm_search():

View file

@ -4,11 +4,11 @@
from typing import List
from metagpt.memory.memory_storage import MemoryStorage
from metagpt.schema import Message
from metagpt.actions import BossRequirement
from metagpt.actions import WritePRD
from metagpt.actions.action_output import ActionOutput
from metagpt.memory.memory_storage import MemoryStorage
from metagpt.schema import Message
def test_idea_message():
@ -26,7 +26,7 @@ def test_idea_message():
sim_idea = 'Write a game of cli snake'
sim_message = Message(role='BOSS', content=sim_idea, cause_by=BossRequirement)
new_messages = memory_storage.search(sim_message)
assert len(new_messages) == 0 # similar, return []
assert len(new_messages) == 0 # similar, return []
new_idea = 'Write a 2048 web game'
new_message = Message(role='BOSS', content=new_idea, cause_by=BossRequirement)
@ -68,7 +68,7 @@ def test_actionout_message():
role='user',
cause_by=WritePRD)
new_messages = memory_storage.search(sim_message)
assert len(new_messages) == 0 # similar, return []
assert len(new_messages) == 0 # similar, return []
new_conent = 'Incorporate basic features of a snake game such as scoring and increasing difficulty'
new_message = Message(content=new_conent,

View file

@ -19,6 +19,7 @@ DETAIL_REQUIREMENT = """需求开发一个基于LLM大语言模型
1. 大语言模型已经有前置的抽象部署可以通过 `from metagpt.llm import LLM`再使用`LLM().ask(prompt)`直接调用
2. Elastic已有[部署](http://192.168.50.82:9200/)代码可以直接使用这个部署"""
PRD = '''## 原始需求
```python
"""
@ -150,6 +151,7 @@ sequenceDiagram
```
'''
TASKS = '''## Logic Analysis
在这个项目中所有的模块都依赖于SearchEngine这是主入口其他的模块IndexRanking和Summary都通过它交互另外"Index"类又依赖于"KnowledgeBase"因为它需要从知识库中获取数据
@ -181,6 +183,7 @@ task_list = [
这个任务列表首先定义了最基础的模块然后是依赖这些模块的模块最后是辅助模块可以根据团队的能力和资源同时开发多个任务只要满足依赖关系例如在开发"search.py"之前可以同时开发"knowledge_base.py""index.py""ranking.py""summary.py"
'''
TASKS_TOMATO_CLOCK = '''## Required Python third-party packages: Provided in requirements.txt format
```python
Flask==2.1.1
@ -221,30 +224,30 @@ task_list = [
TASK = """smart_search_engine/knowledge_base.py"""
STRS_FOR_PARSING = [
"""
## 1
```python
a
```
""",
"""
##2
```python
"a"
```
""",
"""
## 3
```python
a = "a"
```
""",
"""
## 4
```python
a = 'a'
```
"""
"""
## 1
```python
a
```
""",
"""
##2
```python
"a"
```
""",
"""
## 3
```python
a = "a"
```
""",
"""
## 4
```python
a = 'a'
```
"""
]

View file

@ -33,7 +33,7 @@ async def test_engineer():
def test_parse_str():
for idx, i in enumerate(STRS_FOR_PARSING):
text = CodeParser.parse_str(f"{idx + 1}", i)
text = CodeParser.parse_str(f"{idx+1}", i)
# logger.info(text)
assert text == 'a'

View file

@ -12,7 +12,7 @@ async def mock_llm_ask(self, prompt: str, system_msgs):
return '["dataiku", "datarobot"]'
elif "Provide up to 4 queries related to your research topic" in prompt:
return '["Dataiku machine learning platform", "DataRobot AI platform comparison", ' \
'"Dataiku vs DataRobot features", "Dataiku and DataRobot use cases"]'
'"Dataiku vs DataRobot features", "Dataiku and DataRobot use cases"]'
elif "sort the remaining search results" in prompt:
return '[1,2]'
elif "Not relevant." in prompt:

View file

@ -24,4 +24,4 @@ async def test_tutorial_assistant(language: str, topic: str):
title = filename.split("/")[-1].split(".")[0]
async with aiofiles.open(filename, mode="r") as reader:
content = await reader.read()
assert content.startswith(f"# {title}")
assert content.startswith(f"# {title}")

View file

@ -2,8 +2,9 @@
# @Date : 2023/7/22 02:40
# @Author : stellahong (stellahong@fuzhi.ai)
#
from metagpt.roles import ProductManager
from metagpt.software_company import SoftwareCompany
from metagpt.roles import ProductManager
from tests.metagpt.roles.ui_role import UI

View file

@ -248,12 +248,12 @@ class UI(Role):
"""Class representing the UI Role."""
def __init__(
self,
name="Catherine",
profile="UI Design",
goal="Finish a workable and good User Interface design based on a product design",
constraints="Give clear layout description and use standard icons to finish the design",
skills=["SD"],
self,
name="Catherine",
profile="UI Design",
goal="Finish a workable and good User Interface design based on a product design",
constraints="Give clear layout description and use standard icons to finish the design",
skills=["SD"],
):
super().__init__(name, profile, goal, constraints)
self.load_skills(skills)

View file

@ -45,8 +45,7 @@ def test_set_manager(env: Environment):
@pytest.mark.asyncio
async def test_publish_and_process_message(env: Environment):
product_manager = ProductManager("Alice", "Product Manager", "做AI Native产品", "资源有限")
architect = Architect("Bob", "Architect", "设计一个可用、高效、较低成本的系统,包括数据结构与接口",
"资源有限,需要节省成本")
architect = Architect("Bob", "Architect", "设计一个可用、高效、较低成本的系统,包括数据结构与接口", "资源有限,需要节省成本")
env.add_roles([product_manager, architect])
env.set_manager(Manager())

View file

@ -2,10 +2,12 @@ import pytest
import pandas as pd
from pathlib import Path
from tests.data import sales_desc, store_desc
from metagpt.tools.code_interpreter import OpenCodeInterpreter, OpenInterpreterDecorator
from metagpt.actions import Action
from metagpt.logs import logger
logger.add('./tests/data/test_ci.log')
stock = "./tests/data/baba_stock.csv"
@ -36,6 +38,5 @@ async def test_actions():
# 可视化指标结果
figure_path = './tests/data/figure_ci.png'
ci_ploter = OpenCodeInterpreter()
ci_ploter.chat(
f"使用seaborn对{df_path}中与股票布林带有关的数据列的Date, Close, SMA, BB_upper布林带上界, BB_lower布林带下界进行可视化, 可视化图片保存在{figure_path}中。不需要任何指标计算把Date列转换为日期类型。要求图片优美BB_upper, BB_lower之间使用合适的颜色填充。")
ci_ploter.chat(f"使用seaborn对{df_path}中与股票布林带有关的数据列的Date, Close, SMA, BB_upper布林带上界, BB_lower布林带下界进行可视化, 可视化图片保存在{figure_path}中。不需要任何指标计算把Date列转换为日期类型。要求图片优美BB_upper, BB_lower之间使用合适的颜色填充。")
assert Path(figure_path).is_file()

View file

@ -16,8 +16,7 @@ from metagpt.tools.search_engine import SearchEngine
class MockSearchEnine:
async def run(self, query: str, max_results: int = 8, as_string: bool = True) -> str | list[dict[str, str]]:
rets = [{"url": "https://metagpt.com/mock/{i}", "title": query, "snippet": query * i} for i in
range(max_results)]
rets = [{"url": "https://metagpt.com/mock/{i}", "title": query, "snippet": query * i} for i in range(max_results)]
return "\n".join(rets) if as_string else rets
@ -35,7 +34,7 @@ class MockSearchEnine:
(SearchEngineType.DUCK_DUCK_GO, None, 6, False),
(SearchEngineType.CUSTOM_ENGINE, MockSearchEnine().run, 8, False),
(SearchEngineType.CUSTOM_ENGINE, MockSearchEnine().run, 6, False),
],
)
async def test_search_engine(search_engine_typpe, run_func, max_results, as_string, ):

View file

@ -6,6 +6,7 @@
@File : test_custom_decoder.py
"""
from metagpt.utils.custom_decoder import CustomDecoder

View file

@ -23,3 +23,4 @@ async def test_write_and_read_file(root_path: Path, filename: str, content: byte
assert root_path / filename == full_file_name
file_data = await File.read(full_file_name)
assert file_data.decode("utf-8") == content

View file

@ -68,45 +68,44 @@ def test_parse_data():
("text", "data_type", "parsed_data", "expected_exception"),
[
(
"""xxx [1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}] xxx""",
list,
[1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}],
None,
"""xxx [1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}] xxx""",
list,
[1, 2, ["a", "b", [3, 4]], {"x": 5, "y": [6, 7]}],
None,
),
(
"""xxx ["1", "2", "3"] xxx \n xxx \t xx""",
list,
["1", "2", "3"],
None,
"""xxx ["1", "2", "3"] xxx \n xxx \t xx""",
list,
["1", "2", "3"],
None,
),
(
"""{"title": "a", "directory": {"sub_dir1": ["title1, title2"]}, "sub_dir2": [1, 2]}""",
dict,
{"title": "a", "directory": {"sub_dir1": ["title1, title2"]}, "sub_dir2": [1, 2]},
None,
"""{"title": "a", "directory": {"sub_dir1": ["title1, title2"]}, "sub_dir2": [1, 2]}""",
dict,
{"title": "a", "directory": {"sub_dir1": ["title1, title2"]}, "sub_dir2": [1, 2]},
None,
),
(
"""xxx {"title": "x", \n \t "directory": ["x", \n "y"]} xxx \n xxx \t xx""",
dict,
{"title": "x", "directory": ["x", "y"]},
None,
"""xxx {"title": "x", \n \t "directory": ["x", \n "y"]} xxx \n xxx \t xx""",
dict,
{"title": "x", "directory": ["x", "y"]},
None,
),
(
"""xxx xx""",
list,
None,
Exception,
"""xxx xx""",
list,
None,
Exception,
),
(
"""xxx [1, 2, []xx""",
list,
None,
Exception,
"""xxx [1, 2, []xx""",
list,
None,
Exception,
),
]
)
def test_extract_struct(text: str, data_type: Union[type(list), type(dict)], parsed_data: Union[list, dict],
expected_exception):
def test_extract_struct(text: str, data_type: Union[type(list), type(dict)], parsed_data: Union[list, dict], expected_exception):
def case():
resp = OutputParser.extract_struct(text, data_type)
assert resp == parsed_data

View file

@ -52,9 +52,9 @@ PAGE = """
</html>
"""
CONTENT = 'This is a HeadingThis is a paragraph witha linkand someemphasizedtext.Item 1Item 2Item 3Numbered Item 1Numbered ' \
'Item 2Numbered Item 3Header 1Header 2Row 1, Cell 1Row 1, Cell 2Row 2, Cell 1Row 2, Cell 2Name:Email:SubmitThis is a div ' \
'with a class "box".a link'
CONTENT = 'This is a HeadingThis is a paragraph witha linkand someemphasizedtext.Item 1Item 2Item 3Numbered Item 1Numbered '\
'Item 2Numbered Item 3Header 1Header 2Row 1, Cell 1Row 1, Cell 2Row 2, Cell 1Row 2, Cell 2Name:Email:SubmitThis is a div '\
'with a class "box".a link'
def test_web_page():

View file

@ -71,6 +71,7 @@ class Person:
...
'''
merged_code = '''
#!/usr/bin/env python
# -*- coding: utf-8 -*-