Skip to content

Commit a72c61f

Browse files
authored
Add collate file and more tests from autogpt into testbed (microsoft#915)
* Add collate file. * Add requirements.txt, Fix typo, Add tests * More tests. * Update check.py * Update scenario.py * Update prepare_autogpt.py * Update prepare_autogpt.py * More tasks for testset. * Add more tests. * Update docs. * Optimize file organize.
1 parent 579d5c5 commit a72c61f

File tree

29 files changed

+1023
-17
lines changed

29 files changed

+1023
-17
lines changed

samples/tools/testbed/README.md

+17
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,20 @@ python ./run_scenarios.py ./scenarios/GAIA/gaia_validation_level_1__two_agents_g
216216
# Compute Metrics
217217
python utils/collate_gaia_csv.py ./results/gaia_validation_level_1__two_agents_gpt4 | python utils/metrics_gaia.py
218218
```
219+
220+
## (Example) Running tasks from AutoGPT
221+
222+
The Testbed supports running tasks proposed in [AutoGPT benchmark](https://github.com/Significant-Gravitas/AutoGPT/tree/master/benchmark/agbenchmark/challenges). In this scenario, the agents are prompted to handle a diverse range of tasks, including coding, question answering according to given tasks, web scraping. Similar to scenarios in HumanEval, the agents can call the unit test script to check if the task is successfully done.
223+
224+
Accessing this scenario-type requires converting tasks, running the Testbed, collating the results, and finally computing the metrics. The following commands will run each test instance with GPT-4:
225+
226+
```
227+
# Convert tasks
228+
python utils/prepare_autogpt.py
229+
230+
# Run all the scenarios with GPT-4
231+
python run_scenarios.py scenarios/AutoGPT/autogpt_twoagent_gpt4.jsonl
232+
233+
# Compute metrics, the metric script shares the same one with HumanEval
234+
python utils/collate_autogpt.py ./results/autogpt_twoagent_gpt4 | python metrics_human_eval.py
235+
```
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
git+https://github.com/microsoft/autogen.git
2+
pandas
3+
beautifulsoup4
4+
requests
5+
pytest
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
The AutoGPT style tasks are contained in folder `challenges`.
22

3-
Run `python utils/prepare_data.py` to convert the tasks to jsonl format compatible for evaluation.
3+
Run `python ../../utils/prepare_autogpt.py` to convert the tasks to jsonl format compatible for evaluation.

samples/tools/testbed/scenarios/AutoGPT/Templates/TwoAgents/check.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import subprocess
55
import sys
6+
import shutil
67

78

89
def scoring(content: str, should_contain: list, should_not_contain: list):
@@ -28,7 +29,6 @@ def scoring(content: str, should_contain: list, should_not_contain: list):
2829

2930

3031
def check():
31-
workspace = "coding"
3232
files_contents = []
3333
scores = []
3434

@@ -54,9 +54,11 @@ def check():
5454

5555
for file_path in matching_files:
5656
if eval_type == "python":
57+
# copy the test file to working directory
58+
shutil.copy(f"../custom_python/{file_path}", "./")
5759
result = subprocess.run(
5860
[sys.executable, file_path],
59-
cwd=os.path.abspath(workspace),
61+
cwd=os.path.abspath("./"),
6062
capture_output=True,
6163
text=True,
6264
)

samples/tools/testbed/scenarios/AutoGPT/Templates/TwoAgents/scenario.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@
2424
"work_dir": work_dir,
2525
"use_docker": False,
2626
},
27-
max_consecutive_auto_reply=10,
27+
max_consecutive_auto_reply=5,
2828
# default_auto_reply="TERMINATE",
2929
)
3030

3131
if target_folder:
3232
# The tasks involves reading from a file then do sth to it.
3333
message = """
34-
Your task is to: __TASK__ The file you needed is located in this directory: '__TARGET_FOLDER__'. You should save the output files in this directory: './'
35-
Use the following command to check if all the unit tests have passed:
34+
Here is the task description: __TASK__ The file you needed is located in this directory: '__TARGET_FOLDER__'. You should save the output files in the current directory: './'
35+
Run the following command to check if all the unit tests have passed:
3636
```bash
3737
python ../check.py
3838
```
3939
You should refine the code and results until all the tests have passed.
4040
"""
4141
else:
4242
message = """
43-
Your task is to: __TASK__
44-
Use the following command to check if all the unit tests have passed:
43+
Here is the task description: __TASK__
44+
Run the following command to check if all the unit tests have passed:
4545
```bash
4646
python ../check.py
4747
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import unittest
2+
3+
import password_generator
4+
5+
6+
class TestPasswordGenerator(unittest.TestCase):
7+
def test_password_length(self):
8+
for i in range(8, 17):
9+
password = password_generator.generate_password(i)
10+
self.assertEqual(len(password), i)
11+
12+
def test_value_error(self):
13+
with self.assertRaises(ValueError):
14+
password_generator.generate_password(7)
15+
with self.assertRaises(ValueError):
16+
password_generator.generate_password(17)
17+
18+
def test_password_content(self):
19+
password = password_generator.generate_password()
20+
self.assertTrue(any(c.isdigit() for c in password))
21+
self.assertTrue(any(c in password_generator.string.punctuation for c in password))
22+
23+
24+
if __name__ == "__main__":
25+
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"category": [
3+
"coding"
4+
],
5+
"cutoff": 90,
6+
"dependencies": [
7+
"TestThreeSum"
8+
],
9+
"eval_id": "ac75c471-e0ce-400c-ba9a-fb72aaab444f",
10+
"ground": {
11+
"answer": "password_generator.py is created and satisfies the requirements.",
12+
"eval": {
13+
"type": "python"
14+
},
15+
"files": [
16+
"test_pwd.py"
17+
],
18+
"should_contain": [],
19+
"should_not_contain": []
20+
},
21+
"info": {
22+
"description": "Tests if the agent can create a random password generator.",
23+
"difficulty": "basic",
24+
"side_effects": []
25+
},
26+
"name": "PasswordGenerator",
27+
"task": "Create a random password generator. The password should have between 8 and 16 characters and should contain at least one letter, number and symbol. The password should be printed to the console. If no length is specified, the password should be 8 characters long. The password_generator should be imported as a module and called as password = password_generator.generate_password(length=x). Any invalid input should raise a ValueError."
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
import subprocess
3+
import tempfile
4+
import unittest
5+
6+
7+
class TestOrganizeFiles(unittest.TestCase):
8+
def setUp(self):
9+
# Create temporary directory
10+
self.test_dir = tempfile.mkdtemp()
11+
12+
# File types and their corresponding directory
13+
self.file_types = {
14+
"test_image.png": "images",
15+
"test_doc.txt": "documents",
16+
"test_audio.mp3": "audio",
17+
}
18+
19+
# Create test files
20+
for file_name in self.file_types.keys():
21+
open(os.path.join(self.test_dir, file_name), "a").close()
22+
23+
def test_organize_files(self):
24+
# Call the organize_files.py script using subprocess
25+
subprocess.call(["python", "organize_files.py", "--directory_path=" + self.test_dir])
26+
27+
# Check if the files have been moved to the correct directories
28+
for file_name, directory in self.file_types.items():
29+
self.assertTrue(os.path.isfile(os.path.join(self.test_dir, directory, file_name)))
30+
31+
def tearDown(self):
32+
# Delete test directory and its contents
33+
for file_name, directory in self.file_types.items():
34+
os.remove(os.path.join(self.test_dir, directory, file_name))
35+
for directory in set(self.file_types.values()):
36+
os.rmdir(os.path.join(self.test_dir, directory))
37+
os.rmdir(self.test_dir)
38+
39+
40+
if __name__ == "__main__":
41+
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"category": [
3+
"coding",
4+
"general"
5+
],
6+
"cutoff": 90,
7+
"dependencies": [
8+
"TestPasswordGenerator"
9+
],
10+
"eval_id": "029c1e6f-2b36-451e-bca6-60063b827d2e",
11+
"ground": {
12+
"answer": "The correct python file is written and organizes the files accordingly",
13+
"eval": {
14+
"type": "python"
15+
},
16+
"files": [
17+
"test_file_organize.py"
18+
],
19+
"should_contain": [],
20+
"should_not_contain": []
21+
},
22+
"info": {
23+
"description": "Tests if the agent can create a file organizer.",
24+
"difficulty": "basic",
25+
"side_effects": []
26+
},
27+
"name": "FileOrganizer",
28+
"task": "Create a file organizer CLI tool in Python that sorts files in a directory based on their file types (e.g., images, documents, audio) and moves them into these corresponding folders: 'images', 'documents', 'audio'. The entry point will be a python file that can be run this way: python organize_files.py --directory_path=YOUR_DIRECTORY_PATH"
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import unittest
2+
3+
from url_shortener import retrieve_url, shorten_url
4+
5+
6+
class TestURLShortener(unittest.TestCase):
7+
def test_url_retrieval(self):
8+
# Shorten the URL to get its shortened form
9+
shortened_url = shorten_url("https://www.example.com")
10+
11+
# Retrieve the original URL using the shortened URL directly
12+
retrieved_url = retrieve_url(shortened_url)
13+
14+
self.assertEqual(
15+
retrieved_url,
16+
"https://www.example.com",
17+
"Retrieved URL does not match the original!",
18+
)
19+
20+
21+
if __name__ == "__main__":
22+
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"category": [
3+
"coding"
4+
],
5+
"cutoff": 150,
6+
"dependencies": [
7+
"TestFileOrganizer"
8+
],
9+
"eval_id": "8106fd7f-83fd-496e-9513-280f4a3f012c",
10+
"ground": {
11+
"answer": "The correct python file for a basic url shortener CLI",
12+
"eval": {
13+
"type": "python"
14+
},
15+
"files": [
16+
"test_url_shorten.py"
17+
],
18+
"should_contain": [],
19+
"should_not_contain": []
20+
},
21+
"info": {
22+
"description": "Tests if the agent can create a URL shortener.",
23+
"difficulty": "basic",
24+
"side_effects": []
25+
},
26+
"name": "UrlShortener",
27+
"task": "Build a basic URL shortener using a python CLI. Here are the specifications.\n\nFunctionality: The program should have two primary functionalities.\n\nShorten a given URL.\nRetrieve the original URL from a shortened URL.\n\nCLI: The command-line interface should accept a URL as its first input. It should be able to determine if the url is a shortened url or not. If the url is not shortened, it will display ONLY the shortened url, otherwise, it will display ONLY the original unshortened URL. Afterwards, it should prompt the user for another URL to process.\n\nTechnical specifications:\nBuild a file called url_shortener.py. This file will be called through command lines. Do not write your own test cases or any unit test code.\n\nEdge cases:\nFor the sake of simplicity, there will be no edge cases, you can assume the input is always correct and the user immediately passes the shortened version of the url he just shortened.\n\nYou will be expected to create a python file called url_shortener.py that will function through imported as a module.\n\nThe url_shortener.py will be tested this way:\n```\nimport unittest\nfrom url_shortener import shorten_url, retrieve_url\n\nclass TestURLShortener(unittest.TestCase):\n def test_url_retrieval(self):\n # Shorten the URL to get its shortened form\n shortened_url = shorten_url('https://www.example.com')\n\n # Retrieve the original URL using the shortened URL directly\n retrieved_url = retrieve_url(shortened_url)\n\n self.assertEqual(retrieved_url, 'https://www.example.com', \"Retrieved URL does not match the original!\")\n\nif __name__ == \"__main__\":\n unittest.main()\n```"
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import subprocess
2+
3+
import pytest
4+
5+
6+
def run_game_with_inputs(inputs):
7+
# Start the game process
8+
process = subprocess.Popen(
9+
["python", "tic_tac_toe.py"],
10+
stdin=subprocess.PIPE,
11+
stdout=subprocess.PIPE,
12+
stderr=subprocess.PIPE,
13+
text=True,
14+
)
15+
16+
# Send the input moves one by one
17+
output, errors = process.communicate("\n".join(inputs))
18+
19+
# Print the inputs and outputs
20+
print("Inputs:\n", "\n".join(inputs))
21+
print("Output:\n", output)
22+
print("Errors:\n", errors)
23+
24+
return output
25+
26+
27+
@pytest.mark.parametrize(
28+
"inputs, expected_output",
29+
[
30+
(["0,0", "1,0", "0,1", "1,1", "0,2"], "Player 1 won!"),
31+
(["1,0", "0,0", "1,1", "0,1", "2,0", "0,2"], "Player 2 won!"),
32+
(["0,0", "0,1", "0,2", "1,1", "1,0", "1,2", "2,1", "2,0", "2,2"], "Draw"),
33+
],
34+
)
35+
def test_game(inputs, expected_output):
36+
output = run_game_with_inputs(inputs)
37+
assert expected_output in output
38+
39+
40+
if __name__ == "__main__":
41+
pytest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"category": [
3+
"coding",
4+
"general"
5+
],
6+
"cutoff": 150,
7+
"dependencies": [
8+
"TestUrlShortener"
9+
],
10+
"eval_id": "504b1648-e14a-4982-8b27-074598eb4fd0",
11+
"ground": {
12+
"answer": "The correct python file for a TicTacToe game is written",
13+
"eval": {
14+
"type": "python"
15+
},
16+
"files": [
17+
"test_tictactoe.py"
18+
],
19+
"should_contain": [],
20+
"should_not_contain": []
21+
},
22+
"info": {
23+
"description": "Tests if the agent can create Tic-Tac-Toe game",
24+
"difficulty": "basic",
25+
"side_effects": []
26+
},
27+
"name": "TicTacToe",
28+
"task": "Build a Tic-Tac-Toe game using a python CLI. Here are the specifications.\n\nThe Grid: The game board is a 3x3 grid, consisting of 3 rows and 3 columns, creating a total of 9 squares.\n\nPlayers: There are two players. One player uses the number \"1\", and the other player uses the number \"2\".\n\nTaking Turns: Players take turns to put their respective numbers (\"1\" or \"2\") in an empty square of the grid. Once a player has placed their number in a square, it cannot be changed or removed.\n\nObjective: The goal is to get three of your numbers in a row, either horizontally, vertically, or diagonally.\n\nEnd of the Game: The game concludes in one of two ways: One player gets three of their numbers in a row (horizontally, vertically, or diagonally) and is declared the winner.\nAll squares on the grid are filled, and no player has three in a row. This situation is a \"draw\" or a \"tie\".\n\nTechnical specifications:\nBuild a file called tic_tac_toe.py. This file will be called through command lines. You will have to prompt users for their move. Player 1 will always start.\nPlayers will input their move in the following format: \"x,y\" where x and y represent the location in the grid (0,0 is top left, 2,2 is bottom right).\n\nYour primary requirement is to halt the game when appropriate and to print only one of these three exact sentences:\n\n\"Player 1 won!\"\n\"Player 2 won!\"\n\"Draw\"\n\nEdge cases: A player can send an incorrect location. Either the location is incorrect or the square is already filled. In this case, this counts as doing nothing, and the player gets prompted for new locations again.\n\n\nYou will be expected to create a python file called tic_tac_toe.py that will run through command lines by using ```python tic_tac_toe.py```.\n\nHere is an example of how your tic_tac_toe.py game will be tested.\n```\nprocess = subprocess.Popen(\n ['python', 'tic_tac_toe.py'],\n stdout=subprocess.PIPE,\n text=True\n)\n\noutput, _ = process.communicate('\\n'.join([\"0,0\", \"1,0\", \"0,1\", \"1,1\", \"0,2\"]))\n\nassert \"Player 1 won!\" in output\n```"
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ID,Name,Age
2+
101,John,28
3+
102,Alice,34
4+
103,Bob,45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ID,Occupation,Salary
2+
101,Engineer,80000
3+
102,Doctor,120000
4+
103,Lawyer,95000
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"category": [
3+
"data",
4+
"general"
5+
],
6+
"cutoff": 60,
7+
"dependencies": [
8+
"TestSortCsv"
9+
],
10+
"eval_id": "52467beb-b951-4356-9776-9a0ae46bb33b",
11+
"ground": {
12+
"answer": "The csv data is combined",
13+
"eval": {
14+
"type": "file"
15+
},
16+
"files": [
17+
"output.csv"
18+
],
19+
"should_contain": [
20+
"Age,ID,Name,Occupation,Salary\n28,101,John,Engineer,80000\n34,102,Alice,Doctor,120000\n45,103,Bob,Lawyer,95000"
21+
]
22+
},
23+
"info": {
24+
"description": "Tests if the agent can combine data from a csv",
25+
"difficulty": "intermediate",
26+
"side_effects": [
27+
""
28+
]
29+
},
30+
"name": "CombineCsv",
31+
"task": "The csvs 'file1.csv' and 'file2.csv' both have a column 'ID'. Combine these 2 csvs using the 'ID' column. Then sort the rows by 'ID' in ascending order, sort the columns alphabetically. Write the output in 'output.csv'."
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Date,Description,Amount,Category
2+
2023-01-01,Grocery Store,52.3,Groceries
3+
2023-01-02,Pharmacy,12.5,Healthcare
4+
2023-01-03,Gas Station,29.1,Transportation
5+
2023-01-04,Water,19,Utilities
6+
2023-01-05,Grocery Store,60.25,Groceries
7+
2023-01-06,Coffee Shop,4.5,Dining
8+
2023-01-07,Cinema Tickets,20,Entertainment
9+
2023-01-08,Book Store,30.4,Shopping
10+
2023-01-09,Restaurant Dinner,55.8,Dining
11+
2023-01-10,Electric Bill,65.35,Utilities
12+
2023-01-11,Grocery Store,45.1,Groceries

0 commit comments

Comments
 (0)