Merge branch 'minecraft' of github.com:geekan/MetaGPT into minecraft

This commit is contained in:
stellahsr 2023-10-06 15:49:11 +08:00
commit eb9ea304a5
215 changed files with 10530 additions and 1257 deletions

View file

@ -0,0 +1,54 @@
import pytest
from metagpt.actions.clone_function import CloneFunction, run_function_code
source_code = """
import pandas as pd
import ta
def user_indicator():
# 读取股票数据
stock_data = pd.read_csv('./tests/data/baba_stock.csv')
stock_data.head()
# 计算简单移动平均线
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[['Date', 'Close', 'bb_upper', 'bb_middle', 'bb_lower']].head()
"""
template_code = """
def stock_indicator(stock_path: str, indicators=['Simple Moving Average', 'BollingerBands', 'MACD]) -> pd.DataFrame:
import pandas as pd
# here is your code.
"""
def get_expected_res():
import pandas as pd
import ta
# 读取股票数据
stock_data = pd.read_csv('./tests/data/baba_stock.csv')
stock_data.head()
# 计算简单移动平均线
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[['Date', 'Close', 'bb_upper', 'bb_middle', 'bb_lower']].head()
return stock_data
@pytest.mark.asyncio
async def test_clone_function():
clone = CloneFunction()
code = await clone.run(template_code, source_code)
assert 'def ' in code
stock_path = './tests/data/baba_stock.csv'
df, msg = run_function_code(code, 'stock_indicator', stock_path)
assert not msg
expected_df = get_expected_res()
assert df.equals(expected_df)

View file

@ -9,6 +9,7 @@ import pytest
from metagpt.actions.design_api import WriteDesign
from metagpt.logs import logger
from metagpt.schema import Message
from tests.metagpt.actions.mock import PRD_SAMPLE
@ -18,9 +19,10 @@ async def test_design_api():
design_api = WriteDesign("design_api")
result = await design_api.run(prd)
result = await design_api.run([Message(content=prd, instruct_content=None)])
logger.info(result)
assert len(result) > 0
assert result
@pytest.mark.asyncio
@ -28,7 +30,7 @@ async def test_design_api_calculator():
prd = PRD_SAMPLE
design_api = WriteDesign("design_api")
result = await design_api.run(prd)
result = await design_api.run([Message(content=prd, instruct_content=None)])
logger.info(result)
assert len(result) > 10
assert result

View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/9/13 00:26
@Author : fisherdeng
@File : test_detail_mining.py
"""
import pytest
from metagpt.actions.detail_mining import DetailMining
from metagpt.logs import logger
@pytest.mark.asyncio
async def test_detail_mining():
topic = "如何做一个生日蛋糕"
record = "我认为应该先准备好材料,然后再开始做蛋糕。"
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

@ -31,7 +31,7 @@ async def test_write_test():
code_to_test=code,
test_file_name="test_food.py",
source_file_path="/some/dummy/path/cli_snake_game/cli_snake_game/food.py",
workspace="/some/dummy/path/cli_snake_game"
workspace="/some/dummy/path/cli_snake_game",
)
logger.info(test_code)
@ -40,3 +40,18 @@ async def test_write_test():
assert "from cli_snake_game.food import Food" in test_code
assert "class TestFood(unittest.TestCase)" in test_code
assert "def test_generate" in test_code
@pytest.mark.asyncio
async def test_write_code_invalid_code(mocker):
# Mock the _aask method to return an invalid code string
mocker.patch.object(WriteTest, "_aask", return_value="Invalid Code String")
# Create an instance of WriteTest
write_test = WriteTest()
# Call the write_code method
code = await write_test.write_code("Some prompt:")
# Assert that the returned code is the same as the invalid code string
assert code == "Invalid Code String"

View file

@ -0,0 +1,40 @@
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
"""
@Time : 2023/9/6 21:41:34
@Author : Stitch-z
@File : test_write_tutorial.py
"""
from typing import Dict
import pytest
from metagpt.actions.write_tutorial import WriteDirectory, WriteContent
@pytest.mark.asyncio
@pytest.mark.parametrize(
("language", "topic"),
[("English", "Write a tutorial about Python")]
)
async def test_write_directory(language: str, topic: str):
ret = await WriteDirectory(language=language).run(topic=topic)
assert isinstance(ret, dict)
assert "title" in ret
assert "directory" in ret
assert isinstance(ret["directory"], list)
assert len(ret["directory"])
assert isinstance(ret["directory"][0], dict)
@pytest.mark.asyncio
@pytest.mark.parametrize(
("language", "topic", "directory"),
[("English", "Write a tutorial about Python", {"Introduction": ["What is Python?", "Why learn Python?"]})]
)
async def test_write_content(language: str, topic: str, directory: Dict):
ret = await WriteContent(language=language, directory=directory).run(topic=topic)
assert isinstance(ret, str)
assert list(directory.keys())[0] in ret
for value in list(directory.values())[0]:
assert value in ret

View file

@ -0,0 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/9/16 20:03
@Author : femto Zheng
@File : __init__.py
"""

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/9/16 20:03
@Author : femto Zheng
@File : test_basic_planner.py
"""
import pytest
from semantic_kernel.core_skills import FileIOSkill, MathSkill, TextSkill, TimeSkill
from semantic_kernel.planning.action_planner.action_planner import ActionPlanner
from metagpt.actions import BossRequirement
from metagpt.roles.sk_agent import SkAgent
from metagpt.schema import Message
@pytest.mark.asyncio
async def test_action_planner():
role = SkAgent(planner_cls=ActionPlanner)
# let's give the agent 4 skills
role.import_skill(MathSkill(), "math")
role.import_skill(FileIOSkill(), "fileIO")
role.import_skill(TimeSkill(), "time")
role.import_skill(TextSkill(), "text")
task = "What is the sum of 110 and 990?"
role.recv(Message(content=task, cause_by=BossRequirement))
await role._think() # it will choose mathskill.Add
assert "1100" == (await role._act()).content

View file

@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/9/16 20:03
@Author : femto Zheng
@File : test_basic_planner.py
"""
import pytest
from semantic_kernel.core_skills import TextSkill
from metagpt.actions import BossRequirement
from metagpt.const import SKILL_DIRECTORY
from metagpt.roles.sk_agent import SkAgent
from metagpt.schema import Message
@pytest.mark.asyncio
async def test_basic_planner():
task = """
Tomorrow is Valentine's day. I need to come up with a few date ideas. She speaks French so write it in French.
Convert the text to uppercase"""
role = SkAgent()
# let's give the agent some skills
role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "SummarizeSkill")
role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "WriterSkill")
role.import_skill(TextSkill(), "TextSkill")
# using BasicPlanner
role.recv(Message(content=task, cause_by=BossRequirement))
await role._think()
# assuming sk_agent will think he needs WriterSkill.Brainstorm and WriterSkill.Translate
assert "WriterSkill.Brainstorm" in role.plan.generated_plan.result
assert "WriterSkill.Translate" in role.plan.generated_plan.result
# assert "SALUT" in (await role._act()).content #content will be some French

View file

@ -0,0 +1,93 @@
import asyncio
from metagpt.minecraft_team import GameEnvironment
from metagpt.roles.minecraft.action_developer import ActionDeveloper
from metagpt.logs import logger
async def main():
events = [
[
"observe",
{
"voxels": ["grass_block", "dirt", "grass"],
"status": {
"health": 20,
"food": 20,
"saturation": 5,
"oxygen": 20,
"position": {"x": 0.5, "y": 84, "z": -207.5},
"velocity": {"x": 0, "y": -0.0784000015258789, "z": 0},
"yaw": 3.141592653589793,
"pitch": 0,
"onGround": True,
"equipment": [None, None, None, None, None, None],
"name": "bot",
"isInWater": False,
"isInLava": False,
"isCollidedHorizontally": False,
"isCollidedVertically": True,
"biome": "plains",
"entities": {
"chicken": 29.071822119730644,
"sheep": 20.361212992763768,
},
"timeOfDay": "day",
"inventoryUsed": 0,
"elapsedTime": 41,
},
"inventory": {},
"nearbyChests": {"(1344, 64, 1381)": "Unknown"},
"blockRecords": ["grass_block", "dirt", "grass"],
},
]
]
code = """
async function collectBamboo(bot) {
// Equip the iron sword
const ironSword = bot.inventory.findInventoryItem(mcData.itemsByName.iron_sword.id);
await bot.equip(ironSword, "hand");
// Find bamboo plants using the exploreUntil function
const bambooPlants = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {
const bambooPlants = bot.findBlocks({
matching: block => block.name === "bamboo",
maxDistance: 32,
count: 10
});
return bambooPlants.length >= 10 ? bambooPlants : null;
});
if (!bambooPlants) {
bot.chat("Could not find enough bamboo plants.");
return;
}
// Break 10 bamboo plants using the iron sword
for (const bambooPlant of bambooPlants) {
const block = bot.blockAt(bambooPlant);
await bot.dig(block);
}
bot.chat("Broke 10 bamboo plants.");
// Collect the dropped bamboo items
for (const bambooPlant of bambooPlants) {
await bot.pathfinder.goto(new GoalBlock(bambooPlant.x, bambooPlant.y, bambooPlant.z));
}
bot.chat("Collected 10 bamboo.");
}
"""
ad = ActionDeveloper()
ge = GameEnvironment()
ge.update_event(events)
ad.set_memory(shared_memory=ge)
msg = ad.encapsule_message(events=ge.event, code=code)
logger.info(f"Encapsuled_message: {msg}")
parsed_result = await ad.generate_action_code(**msg)
logger.info(f"Parsed_code_updating: {parsed_result}")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -0,0 +1,64 @@
import asyncio
from metagpt.minecraft_team import GameEnvironment
from metagpt.roles.minecraft.critic_agent import CriticReviewer
from metagpt.logs import logger
async def main():
events = [
[
"observe",
{
"voxels": ["grass_block", "dirt", "grass"],
"status": {
"health": 20,
"food": 20,
"saturation": 5,
"oxygen": 20,
"position": {"x": 0.5, "y": 84, "z": -207.5},
"velocity": {"x": 0, "y": -0.0784000015258789, "z": 0},
"yaw": 3.141592653589793,
"pitch": 0,
"onGround": True,
"equipment": [None, None, None, None, None, None],
"name": "bot",
"isInWater": False,
"isInLava": False,
"isCollidedHorizontally": False,
"isCollidedVertically": True,
"biome": "plains",
"entities": {
"chicken": 29.071822119730644,
"sheep": 20.361212992763768,
},
"timeOfDay": "day",
"inventoryUsed": 0,
"elapsedTime": 41,
},
"inventory": {},
"nearbyChests": {"(1344, 64, 1381)": "Unknown"},
"blockRecords": ["grass_block", "dirt", "grass"],
},
]
]
task = "Obtain 3 more spruce logs"
chest_observation = "Chests: None\n\n"
context = "Question: How to obtain 3 more spruce logs in Minecraft?\nAnswer: You can obtain more spruce logs in Minecraft by finding and chopping down spruce trees in a spruce forest biome. If you have already chopped down all the spruce trees in the area, you can either explore further to find more spruce trees or plant saplings and wait for them to grow into trees."
cr = CriticReviewer()
ge = GameEnvironment()
ge.update_event(events)
cr.set_memory(shared_memory=ge)
msg = cr.encapsule_message(
events=ge.event, task=task, context=context, chest_observation=chest_observation
)
logger.info(f"Encapsuled_message: {msg}")
verify = await cr.verify_task(**msg)
logger.info(f"Parsed_code_updating: {verify}")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -0,0 +1,67 @@
import asyncio
from metagpt.minecraft_team import GameEnvironment
from metagpt.roles.minecraft.curriculum_agent import CurriculumDesigner
from metagpt.logs import logger
async def main():
events = [
[
"observe",
{
"voxels": ["grass_block", "dirt", "grass"],
"status": {
"health": 20,
"food": 20,
"saturation": 5,
"oxygen": 20,
"position": {"x": 0.5, "y": 84, "z": -207.5},
"velocity": {"x": 0, "y": -0.0784000015258789, "z": 0},
"yaw": 3.141592653589793,
"pitch": 0,
"onGround": True,
"equipment": [None, None, None, None, None, None],
"name": "bot",
"isInWater": False,
"isInLava": False,
"isCollidedHorizontally": False,
"isCollidedVertically": True,
"biome": "plains",
"entities": {
"chicken": 29.071822119730644,
"sheep": 20.361212992763768,
},
"timeOfDay": "day",
"inventoryUsed": 0,
"elapsedTime": 41,
},
"inventory": {},
"nearbyChests": {"(1344, 64, 1381)": "Unknown"},
"blockRecords": ["grass_block", "dirt", "grass"],
},
]
]
cd = CurriculumDesigner()
ge = GameEnvironment()
ge.update_event(events)
cd.set_memory(shared_memory=ge)
task_msg = cd.encapsule_design_task_message(
events=ge.event, chest_observation=ge.chest_observation
)
logger.info(f"Encapsuled_design_task_message: {task_msg}")
task = await cd.handle_task_design(**task_msg)
logger.info(f"Design_task_updating: {task}")
context_msg = cd.encapsule_design_curriculum_message(
events=ge.event, chest_observation=ge.chest_observation
)
logger.info(f"Encapsuled_design_context_message: {context_msg}")
context = await cd.handle_curriculum_design(**context_msg)
logger.info(f"Design_context_updating: {context}")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -0,0 +1,93 @@
import asyncio
from metagpt.minecraft_team import GameEnvironment
from metagpt.roles.minecraft.skill_manager import SkillManager
from metagpt.logs import logger
from metagpt.actions.minecraft.manage_skills import (
GenerateSkillDescription,
RetrieveSkills,
AddNewSkills,
)
async def main():
events = [
[
"observe",
{
"voxels": ["grass_block", "dirt", "grass"],
"status": {
"health": 20,
"food": 20,
"saturation": 5,
"oxygen": 20,
"position": {"x": 0.5, "y": 84, "z": -207.5},
"velocity": {"x": 0, "y": -0.0784000015258789, "z": 0},
"yaw": 3.141592653589793,
"pitch": 0,
"onGround": True,
"equipment": [None, None, None, None, None, None],
"name": "bot",
"isInWater": False,
"isInLava": False,
"isCollidedHorizontally": False,
"isCollidedVertically": True,
"biome": "plains",
"entities": {
"chicken": 29.071822119730644,
"sheep": 20.361212992763768,
},
"timeOfDay": "day",
"inventoryUsed": 0,
"elapsedTime": 41,
},
"inventory": {},
"nearbyChests": {"(1344, 64, 1381)": "Unknown"},
"blockRecords": ["grass_block", "dirt", "grass"],
},
]
]
program_code = 'async function obtainSpruceLogs(bot) {\n // Find 3 spruce_log blocks\n const spruceLogs = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {\n const spruceLog = bot.findBlock({\n matching: mcData.blocksByName["spruce_log"].id,\n maxDistance: 32,\n count: 3\n });\n return spruceLog ? spruceLog : null;\n });\n if (spruceLogs) {\n // Mine the spruce_log blocks\n await mineBlock(bot, "spruce_log", 3);\n bot.chat("3 spruce logs obtained.");\n } else {\n bot.chat("Could not find enough spruce logs.");\n }\n}'
program_name = "obtainSpruceLogs"
task = "Obtain 3 more spruce logs"
skills = {
"mineWoodLog": {
"code": 'async function mineWoodLog(bot) {\n const woodLogNames = ["oak_log", "birch_log", "spruce_log", "jungle_log", "acacia_log", "dark_oak_log", "mangrove_log"];\n\n // Find a wood log block\n const woodLog = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {\n for (const name of woodLogNames) {\n const log = bot.findBlock({\n matching: mcData.blocksByName[name].id,\n maxDistance: 32\n });\n if (log) {\n return log;\n }\n }\n return null;\n });\n if (woodLog) {\n // Mine the wood log block\n await mineBlock(bot, woodLog.name, 1);\n bot.chat("Wood log mined.");\n } else {\n bot.chat("Could not find a wood log.");\n }\n}',
"description": "async function mineWoodLog(bot) {\n // The function is about mining a wood log block. It searches for a wood log block by exploring the environment until it finds one of the seven types of wood logs. Once a wood log block is found, it is mined and a message is sent to the chat. If a wood log block is not found, a message is sent to the chat indicating that it could not be found.\n}",
},
"obtainSpruceLogs": {
"code": 'async function obtainSpruceLogs(bot) {\n // Find 3 spruce_log blocks\n const spruceLogs = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {\n const spruceLog = bot.findBlock({\n matching: mcData.blocksByName["spruce_log"].id,\n maxDistance: 32,\n count: 3\n });\n return spruceLog ? spruceLog : null;\n });\n if (spruceLogs) {\n // Mine the spruce_log blocks\n await mineBlock(bot, "spruce_log", 3);\n bot.chat("3 spruce logs obtained.");\n } else {\n bot.chat("Could not find enough spruce logs.");\n }\n}',
"description": "async function obtainSpruceLogs(bot) {\n // The function is about obtaining 3 spruce logs. It explores the environment until it finds 3 spruce_log blocks within a certain distance. Once the blocks are found, it mines them and sends a message indicating that 3 spruce logs have been obtained. If the blocks are not found, it sends a message indicating that it could not find enough spruce logs.\n}",
},
}
context = "Question: How to obtain 3 more spruce logs in Minecraft?\nAnswer: You can obtain more spruce logs in Minecraft by finding and chopping down spruce trees in a spruce forest biome. If you have already chopped down all the spruce trees in the area, you can either explore further to find more spruce trees or plant saplings and wait for them to grow into trees."
sm = SkillManager()
ge = GameEnvironment()
ge.update_event(events)
sm.set_memory(shared_memory=ge)
generate_skill_message = sm.encapsule_message(program_code, program_name)
logger.info(f"Generate_skill_message: {generate_skill_message}")
desp = await sm.generate_skill_descp(**generate_skill_message)
logger.info(f"Generate_skill_descp UPDATING: {desp}")
add_new_skills_message = {
"task": task,
"program_name": program_name,
"program_code": program_code,
"skills": skills,
}
logger.info(f"Handle_add_new_skills_message: {add_new_skills_message}")
new_skills_info = await sm.handle_add_new_skills(**add_new_skills_message)
logger.info(f"Handle_add_new_skills UPDATING: {new_skills_info}")
retrieve_skills_message_step1 = {"query": context}
logger.info(f"Retrieve_skills_message: {retrieve_skills_message_step1}")
skills = await sm.retrieve_skills(**retrieve_skills_message_step1)
logger.info(f"Retrieve_skills UPDATING: {skills}")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -18,4 +18,4 @@ async def test_product_manager():
rsp = await product_manager.handle(MockMessages.req)
logger.info(rsp)
assert len(rsp.content) > 0
assert "产品目标" in rsp.content
assert "Product Goals" in rsp.content

View file

@ -0,0 +1,27 @@
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
"""
@Time : 2023/9/6 23:11:27
@Author : Stitch-z
@File : test_tutorial_assistant.py
"""
import aiofiles
import pytest
from metagpt.roles.tutorial_assistant import TutorialAssistant
@pytest.mark.asyncio
@pytest.mark.parametrize(
("language", "topic"),
[("Chinese", "Write a tutorial about Python")]
)
async def test_tutorial_assistant(language: str, topic: str):
topic = "Write a tutorial about MySQL"
role = TutorialAssistant(language=language)
msg = await role.run(topic)
filename = msg.content
title = filename.split("/")[-1].split(".")[0]
async with aiofiles.open(filename, mode="r") as reader:
content = await reader.read()
assert content.startswith(f"# {title}")

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# @Date : 2023/09/28 00:03
# @Author : yuymf
# @Desc :
import asyncio
from metagpt.logs import logger
from metagpt.minecraft_team import GameEnvironment
async def main():
test_code = "bot.chat(`/time set ${getNextTime()}`);"
mc_port = 2745
ge = GameEnvironment()
ge.set_mc_port(mc_port)
ge.update_code(test_code)
result = await ge.on_event()
logger.info("On event test done")
if __name__ == "__main__":
asyncio.run(main())

View file

@ -0,0 +1,42 @@
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"
# TODO: 需要一种表格数据格式能够支持schame管理的标注字段类型和字段含义。
class CreateStockIndicators(Action):
@OpenInterpreterDecorator(save_code=True, code_file_path="./tests/data/stock_indicators.py")
async def run(self, stock_path: str, indicators=['Simple Moving Average', 'BollingerBands']) -> pd.DataFrame:
"""对stock_path中的股票数据, 使用pandas和ta计算indicators中的技术指标, 返回带有技术指标的股票数据,不需要去除空值, 不需要安装任何包;
指标生成对应的三列: SMA, BB_upper, BB_lower
"""
...
@pytest.mark.asyncio
async def test_actions():
# 计算指标
indicators = ['Simple Moving Average', 'BollingerBands']
stocker = CreateStockIndicators()
df, msg = await stocker.run(stock, indicators=indicators)
assert isinstance(df, pd.DataFrame)
assert 'Close' in df.columns
assert 'Date' in df.columns
# 将df保存为文件将文件路径传入到下一个action
df_path = './tests/data/stock_indicators.csv'
df.to_csv(df_path)
assert Path(df_path).is_file()
# 可视化指标结果
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之间使用合适的颜色填充。")
assert Path(figure_path).is_file()

View file

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
# @Date : 2023/09/28 00:08
# @Author : yuymf
# @Desc :
from metagpt.utils.minecraft import parse_js_code, parse_action_response
from metagpt.logs import logger
from typing import Any
if __name__ == "__main__":
msg = '''
Explain: The code from the last round is a function called `collectBamboo` that is supposed to collect bamboo plants. It equips an iron sword, finds bamboo plants using the `exploreUntil` function, breaks 10 bamboo plants using the iron sword, and then collects the dropped bamboo items.
Plan:
1) Check if the bot has an iron sword in its inventory. If not, collect the necessary materials and craft an iron sword using the `craftItem` function.
2) Use the `exploreUntil` function to find at least 10 bamboo plants. If the function times out or cannot find enough bamboo plants, return and chat "Could not find enough bamboo plants."
3) Equip the iron sword.
4) Iterate over the found bamboo plants and break them using the iron sword.
5) Chat "Broke 10 bamboo plants."
6) Iterate over the found bamboo plants and collect the dropped bamboo items.
7) Chat "Collected 10 bamboo."
Code:
```javascript
async function collectBamboo(bot) {
// Check if the bot has an iron sword
const ironSword = bot.inventory.findInventoryItem(mcData.itemsByName.iron_sword.id);
if (!ironSword) {
// Collect the necessary materials to craft an iron sword
await mineBlock(bot, "iron_ore", 3);
await smeltItem(bot, "iron_ore", "oak_planks", 3);
await craftItem(bot, "iron_sword", 1);
}
// Find bamboo plants using the exploreUntil function
const bambooPlants = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {
const bambooPlants = bot.findBlocks({
matching: block => block.name === "bamboo",
maxDistance: 32,
count: 10
});
return bambooPlants.length >= 10 ? bambooPlants : null;
});
if (!bambooPlants) {
bot.chat("Could not find enough bamboo plants.");
return;
}
// Equip the iron sword
await bot.equip(ironSword, "hand");
// Break 10 bamboo plants using the iron sword
for (const bambooPlant of bambooPlants) {
const block = bot.blockAt(bambooPlant);
await bot.dig(block);
}
bot.chat("Broke 10 bamboo plants.");
// Collect the dropped bamboo items
for (const bambooPlant of bambooPlants) {
await bot.pathfinder.goto(new GoalBlock(bambooPlant.x, bambooPlant.y, bambooPlant.z));
}
bot.chat("Collected 10 bamboo.");
}
```
'''
logger.info(f"Parse_js_code result is HERE: {parse_js_code(msg)}")
logger.info(f"Parse_action_response result is HERE: {parse_action_response(msg)}")

View file

@ -0,0 +1,72 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/9/8 11:38
@Author : femto Zheng
@File : test_custom_decoder.py
"""
from metagpt.utils.custom_decoder import CustomDecoder
def test_parse_single_quote():
# Create a custom JSON decoder
decoder = CustomDecoder(strict=False)
# Your provided input with single-quoted strings and line breaks
input_data = """{'a"
b':'"title": "Reach and engagement of campaigns",
"x-axis": "Low Reach --> High Reach",
"y-axis": "Low Engagement --> High Engagement",
"quadrant-1": "We should expand",
"quadrant-2": "Need to promote",
"quadrant-3": "Re-evaluate",
"quadrant-4": "May be improved",
"Campaign: A": [0.3, 0.6],
"Campaign B": [0.45, 0.23],
"Campaign C": [0.57, 0.69],
"Campaign D": [0.78, 0.34],
"Campaign E": [0.40, 0.34],
"Campaign F": [0.35, 0.78],
"Our Target Product": [0.5, 0.6]
'
}
"""
# Parse the JSON using the custom decoder
parsed_data = decoder.decode(input_data)
assert 'a"\n b' in parsed_data
def test_parse_triple_double_quote():
# Create a custom JSON decoder
decoder = CustomDecoder(strict=False)
# Your provided input with single-quoted strings and line breaks
input_data = '{"""a""":"b"}'
# Parse the JSON using the custom decoder
parsed_data = decoder.decode(input_data)
assert "a" in parsed_data
input_data = '{"""a""":"""b"""}'
# Parse the JSON using the custom decoder
parsed_data = decoder.decode(input_data)
assert parsed_data["a"] == "b"
def test_parse_triple_single_quote():
# Create a custom JSON decoder
decoder = CustomDecoder(strict=False)
# Your provided input with single-quoted strings and line breaks
input_data = "{'''a''':'b'}"
# Parse the JSON using the custom decoder
parsed_data = decoder.decode(input_data)
assert "a" in parsed_data
input_data = "{'''a''':'''b'''}"
# Parse the JSON using the custom decoder
parsed_data = decoder.decode(input_data)
assert parsed_data["a"] == "b"

View file

@ -0,0 +1,26 @@
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
"""
@Time : 2023/9/4 15:40:40
@Author : Stitch-z
@File : test_file.py
"""
from pathlib import Path
import pytest
from metagpt.utils.file import File
@pytest.mark.asyncio
@pytest.mark.parametrize(
("root_path", "filename", "content"),
[(Path("/code/MetaGPT/data/tutorial_docx/2023-09-07_17-05-20"), "test.md", "Hello World!")]
)
async def test_write_and_read_file(root_path: Path, filename: str, content: bytes):
full_file_name = await File.write(root_path=root_path, filename=filename, content=content.encode('utf-8'))
assert isinstance(full_file_name, Path)
assert root_path / filename == full_file_name
file_data = await File.read(full_file_name)
assert file_data.decode("utf-8") == content

View file

@ -0,0 +1,60 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2023/9/11 11:53
@Author : femto Zheng
@File : test_json_to_markdown.py
"""
from metagpt.utils.json_to_markdown import json_to_markdown
def test_json_to_markdown():
# Example nested JSON data
json_data = {
"title": "Sample JSON to Markdown Conversion",
"description": "Convert JSON to Markdown with headings and lists.",
"tags": ["json", "markdown", "conversion"],
"content": {
"section1": {"subsection1": "This is a subsection.", "subsection2": "Another subsection."},
"section2": "This is the second section content.",
},
}
# Convert JSON to Markdown with nested sections
markdown_output = json_to_markdown(json_data)
expected = """## title
Sample JSON to Markdown Conversion
## description
Convert JSON to Markdown with headings and lists.
## tags
- json
- markdown
- conversion
## content
### section1
#### subsection1
This is a subsection.
#### subsection2
Another subsection.
### section2
This is the second section content.
"""
# Print or use the generated Markdown
# print(markdown_output)
assert expected == markdown_output

View file

@ -5,7 +5,7 @@
@Author : chengmaoyu
@File : test_output_parser.py
"""
from typing import List, Tuple
from typing import List, Tuple, Union
import pytest
@ -64,6 +64,59 @@ def test_parse_data():
assert OutputParser.parse_data(test_data) == expected_result
@pytest.mark.parametrize(
("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", "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,
),
(
"""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 [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 case():
resp = OutputParser.extract_struct(text, data_type)
assert resp == parsed_data
if expected_exception:
with pytest.raises(expected_exception):
case()
else:
case()
if __name__ == '__main__':
t_text = '''
## Required Python third-party packages