From a60666b7fc0473a6e6c847c0357176ebca5ed16c Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sat, 22 Apr 2023 20:08:55 +0200 Subject: [PATCH 1/6] Update .gitignore Ignores Python project common files --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 0d20b64..54bcb8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ +__pycache__/ *.pyc +*.pyo +*.egg-info/ +dist/ +build/ +.env +venv/ \ No newline at end of file From 805a52ff69e75e8f86a1857eb371b43c38ec7c2d Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sat, 22 Apr 2023 20:09:45 +0200 Subject: [PATCH 2/6] Modularization --- ai/__init__.py | 1 + ai_functions.py => ai/ai_functions.py | 0 test_ai_function.py | 171 -------------------------- tests/__init__.py | 1 + 4 files changed, 2 insertions(+), 171 deletions(-) create mode 100644 ai/__init__.py rename ai_functions.py => ai/ai_functions.py (100%) delete mode 100644 test_ai_function.py create mode 100644 tests/__init__.py diff --git a/ai/__init__.py b/ai/__init__.py new file mode 100644 index 0000000..b705176 --- /dev/null +++ b/ai/__init__.py @@ -0,0 +1 @@ +from .ai_functions import ai_function \ No newline at end of file diff --git a/ai_functions.py b/ai/ai_functions.py similarity index 100% rename from ai_functions.py rename to ai/ai_functions.py diff --git a/test_ai_function.py b/test_ai_function.py deleted file mode 100644 index bb2e19d..0000000 --- a/test_ai_function.py +++ /dev/null @@ -1,171 +0,0 @@ -import ast -import json -import time -import ai_functions -import pytest -import openai -import keys - -# Initialize the OpenAI API client -openai.api_key = keys.OPENAI_API_KEY - -# Run all tests, print the results, and return the number of failed tests -def run_tests(model): - test_functions = [test_1, test_2, test_3, test_4, test_5, test_6] - test_names = [ - "Generate fake people", - "Generate Random Password", - "Calculate area of triangle", - "Calculate the nth prime number", - "Encrypt text", - "Find missing numbers" -] - failed_tests = [] - - i = 0 - for test in test_functions: - print(f"=-=-=- Running test: {test.__name__} - {test_names[i]} with model {model} -=-=-=") - i += 1 - try: - test(model) - print(f"{test.__name__}: PASSED") - except AssertionError as e: - print(f"{test.__name__}: FAILED - {e}") - failed_tests.append(test) - # Wait so as not to exceed the API rate limit - time.sleep(1) - - # Print the total number of tests - print(f"Total tests: {len(test_functions)}") - - # Print the number of failed tests - print(f"Success Rate: {len(test_functions) - len(failed_tests)}/{len(test_functions)}") - -# Ai function test 1 -def test_1(model): - function_string = "def fake_people(n: int) -> list[dict]:" - args = ["4"] - description_string = """Generates n examples of fake data representing people, - each with a name and an age.""" - - result_string = ai_functions.ai_function(function_string, args, description_string, model) - - print(f"Output: {result_string}") - # Assert the result can be parsed as is a list of dictionaries - print("Testing if result is a a string...") - assert isinstance(result_string, str) - result = None - try: - print("Testing if result can be parsed as a list of dictionaries...") - # Parse the result as a list of dictionaries - result = json.loads(result_string) - except Exception as e: - # If the result can't be parsed as a list of dictionaries, the test fails - assert False - - # Assert the length of the result is equal to the number of people requested - print("Testing if the length of the result is equal to the number of people requested...") - if result: - assert len(result) == int(args[0]) - else: - assert False - -# Ai function test 2 -def test_2(model): - function_string = "def random_password_generator(length: int, special_chars: bool) -> str:" - args = ["12", "True"] - description_string = """Generates a random password of given length with or without special characters.""" - - result_string = ai_functions.ai_function(function_string, args, description_string, model) - - print(f"Output: {result_string}") - - # Assert the length of the result is equal to the length requested - print("Testing if the length of the result is equal to the length requested...") - assert len(result_string) == int(args[0]) - -# Ai function test 3 -def test_3(model): - function_string = "def calculate_area_of_triangle(base: float, height: float) -> float:" - args = ["15", "6.5"] - description_string = """Calculates the area of a triangle given its base and height.""" - - result_string = ai_functions.ai_function(function_string, args, description_string, model) - print(f"Output: {result_string}") - - # Assert the result can be parsed as a float - print("Testing if result is a a float...") - try: - assert isinstance(float(result_string), float) - except Exception as e: - print(e) - assert False - - # Assert the result is equal to the expected area of the triangle - expected_area = (float(args[0]) * float(args[1])) / 2 - print("Testing if the result is equal to the expected area of the triangle, which is: " + str(expected_area)) - assert float(result_string) == pytest.approx(expected_area) - -# Ai function test 4 -def test_4(model): - function_string = "def get_nth_prime_number(n: int) -> int:" - args = ["10"] - description_string = """Finds and returns the nth prime number.""" - - result_string = ai_functions.ai_function(function_string, args, description_string, model) - - print(f"Output: {result_string}") - - # Assert the result can be parsed as an integer - print("Testing if result is a a integer...") - try: - assert isinstance(int(result_string), int) - except Exception as e: - print(e) - assert False - - # Assert the result is equal to the expected nth prime number - expected_prime_number = 29 - print("Testing if the result is equal to the expected nth prime number, which is: " + str(expected_prime_number)) - assert int(result_string) == expected_prime_number - -# Ai function test 5 -def test_5(model): - function_string = "def encrypt_text(text: str, key: str) -> str:" - args = ["'Hello, World!'", "'abc123'"] - description_string = """Encrypts the given text using a simple character substitution based on the provided key.""" - - result_string = ai_functions.ai_function(function_string, args, description_string, model) - - print(f"Output: {result_string}") - - # Assert the result has the same length as the input text - print("Testing if the result has the same length as the input text...") - assert len(result_string) == len(args[0]) - -# Ai function test 6 -def test_6(model): - function_string = "def find_missing_numbers_in_list(numbers: list[int]) -> list[int]:" - args = ["[3, 5, 8, 15, 16]"] - description_string = """Finds and returns a list of missing numbers in a given sorted list.""" - - result_string = ai_functions.ai_function(function_string, args, description_string, model) - - print(f"Output: {result_string}") - - # Assert the result can be parsed as a list - try: - result_list = ast.literal_eval(result_string) - print("Testing if result is a a list...") - assert isinstance(result_list, list) - except Exception as e: - print(e) - assert False - - # Assert the result list contains the expected missing numbers - expected_missing_numbers = [4, 6, 7, 9, 10, 11, 12, 13, 14] - print("Testing if the result list contains the expected missing numbers...") - assert result_list == expected_missing_numbers - -run_tests("gpt-4") -run_tests("gpt-3.5-turbo") \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..ddd3474 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +from .test_ai_function import test_1, test_2, test_3, test_4 \ No newline at end of file From b0d98b57448526f7d5fe643a7e469d52eba0a5c3 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sat, 22 Apr 2023 20:11:04 +0200 Subject: [PATCH 3/6] Introduces .env file --- tests/test_ai_function.py | 173 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 tests/test_ai_function.py diff --git a/tests/test_ai_function.py b/tests/test_ai_function.py new file mode 100644 index 0000000..a754653 --- /dev/null +++ b/tests/test_ai_function.py @@ -0,0 +1,173 @@ +import ast +import json +import time +import ai.ai_functions as ai_functions +import pytest +import openai +from dotenv import load_dotenv +from os import environ +load_dotenv() + +# Initialize the OpenAI API client +openai.api_key = environ.get("OPENAI_API_KEY") + +# Run all tests, print the results, and return the number of failed tests +def run_tests(model): + test_functions = [test_1, test_2, test_3, test_4, test_5, test_6] + test_names = [ + "Generate fake people", + "Generate Random Password", + "Calculate area of triangle", + "Calculate the nth prime number", + "Encrypt text", + "Find missing numbers" +] + failed_tests = [] + + i = 0 + for test in test_functions: + print(f"=-=-=- Running test: {test.__name__} - {test_names[i]} with model {model} -=-=-=") + i += 1 + try: + test(model) + print(f"{test.__name__}: PASSED") + except AssertionError as e: + print(f"{test.__name__}: FAILED - {e}") + failed_tests.append(test) + # Wait so as not to exceed the API rate limit + time.sleep(1) + + # Print the total number of tests + print(f"Total tests: {len(test_functions)}") + + # Print the number of failed tests + print(f"Success Rate: {len(test_functions) - len(failed_tests)}/{len(test_functions)}") + +# Ai function test 1 +def test_1(model): + function_string = "def fake_people(n: int) -> list[dict]:" + args = ["4"] + description_string = """Generates n examples of fake data representing people, + each with a name and an age.""" + + result_string = ai_functions.ai_function(function_string, args, description_string, model) + + print(f"Output: {result_string}") + # Assert the result can be parsed as is a list of dictionaries + print("Testing if result is a a string...") + assert isinstance(result_string, str) + result = None + try: + print("Testing if result can be parsed as a list of dictionaries...") + # Parse the result as a list of dictionaries + result = json.loads(result_string) + except Exception as e: + # If the result can't be parsed as a list of dictionaries, the test fails + assert False + + # Assert the length of the result is equal to the number of people requested + print("Testing if the length of the result is equal to the number of people requested...") + if result: + assert len(result) == int(args[0]) + else: + assert False + +# Ai function test 2 +def test_2(model): + function_string = "def random_password_generator(length: int, special_chars: bool) -> str:" + args = ["12", "True"] + description_string = """Generates a random password of given length with or without special characters.""" + + result_string = ai_functions.ai_function(function_string, args, description_string, model) + + print(f"Output: {result_string}") + + # Assert the length of the result is equal to the length requested + print("Testing if the length of the result is equal to the length requested...") + assert len(result_string) == int(args[0]) + +# Ai function test 3 +def test_3(model): + function_string = "def calculate_area_of_triangle(base: float, height: float) -> float:" + args = ["15", "6.5"] + description_string = """Calculates the area of a triangle given its base and height.""" + + result_string = ai_functions.ai_function(function_string, args, description_string, model) + print(f"Output: {result_string}") + + # Assert the result can be parsed as a float + print("Testing if result is a a float...") + try: + assert isinstance(float(result_string), float) + except Exception as e: + print(e) + assert False + + # Assert the result is equal to the expected area of the triangle + expected_area = (float(args[0]) * float(args[1])) / 2 + print("Testing if the result is equal to the expected area of the triangle, which is: " + str(expected_area)) + assert float(result_string) == pytest.approx(expected_area) + +# Ai function test 4 +def test_4(model): + function_string = "def get_nth_prime_number(n: int) -> int:" + args = ["10"] + description_string = """Finds and returns the nth prime number.""" + + result_string = ai_functions.ai_function(function_string, args, description_string, model) + + print(f"Output: {result_string}") + + # Assert the result can be parsed as an integer + print("Testing if result is a a integer...") + try: + assert isinstance(int(result_string), int) + except Exception as e: + print(e) + assert False + + # Assert the result is equal to the expected nth prime number + expected_prime_number = 29 + print("Testing if the result is equal to the expected nth prime number, which is: " + str(expected_prime_number)) + assert int(result_string) == expected_prime_number + +# Ai function test 5 +def test_5(model): + function_string = "def encrypt_text(text: str, key: str) -> str:" + args = ["'Hello, World!'", "'abc123'"] + description_string = """Encrypts the given text using a simple character substitution based on the provided key.""" + + result_string = ai_functions.ai_function(function_string, args, description_string, model) + + print(f"Output: {result_string}") + + # Assert the result has the same length as the input text + print("Testing if the result has the same length as the input text...") + assert len(result_string) == len(args[0]) + +# Ai function test 6 +def test_6(model): + function_string = "def find_missing_numbers_in_list(numbers: list[int]) -> list[int]:" + args = ["[3, 5, 8, 15, 16]"] + description_string = """Finds and returns a list of missing numbers in a given sorted list.""" + + result_string = ai_functions.ai_function(function_string, args, description_string, model) + + print(f"Output: {result_string}") + + # Assert the result can be parsed as a list + try: + result_list = ast.literal_eval(result_string) + print("Testing if result is a a list...") + assert isinstance(result_list, list) + except Exception as e: + print(e) + assert False + + # Assert the result list contains the expected missing numbers + expected_missing_numbers = [4, 6, 7, 9, 10, 11, 12, 13, 14] + print("Testing if the result list contains the expected missing numbers...") + assert result_list == expected_missing_numbers + +run_tests("gpt-4") +run_tests("gpt-3.5-turbo") \ No newline at end of file From a74dde0a86d94169e8d8d80a4754d1d353ef6ff9 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sat, 22 Apr 2023 20:11:38 +0200 Subject: [PATCH 4/6] Update requirements.txt Adds python-dotenv --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index ff11a64..f0e766e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ openai==0.27.0 pytest==7.2.2 +python-dotenv==0.19.1 From ff79465a0cea19eb246f193b6b2447df1e432ce0 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sat, 22 Apr 2023 20:13:07 +0200 Subject: [PATCH 5/6] Adds doc-strings --- ai/ai_functions.py | 2 +- tests/test_ai_function.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ai/ai_functions.py b/ai/ai_functions.py index a0d53a3..5e4a1c1 100644 --- a/ai/ai_functions.py +++ b/ai/ai_functions.py @@ -1,7 +1,7 @@ import openai def ai_function(function, args, description, model = "gpt-4"): - # parse args to comma separated string + """Takes a function, arguments, and a description and returns the result of the function with the given arguments.""" args = ", ".join(args) messages = [{"role": "system", "content": f"You are now the following python function: ```# {description}\n{function}```\n\nOnly respond with your `return` value. Do not include any other explanatory text in your response."},{"role": "user", "content": args}] diff --git a/tests/test_ai_function.py b/tests/test_ai_function.py index a754653..25d05a9 100644 --- a/tests/test_ai_function.py +++ b/tests/test_ai_function.py @@ -13,6 +13,7 @@ # Run all tests, print the results, and return the number of failed tests def run_tests(model): + """Runs all tests and prints the results. Returns the number of failed tests.""" test_functions = [test_1, test_2, test_3, test_4, test_5, test_6] test_names = [ "Generate fake people", @@ -45,6 +46,7 @@ def run_tests(model): # Ai function test 1 def test_1(model): + """Generates n examples of fake data representing people, each with a name and an age.""" function_string = "def fake_people(n: int) -> list[dict]:" args = ["4"] description_string = """Generates n examples of fake data representing people, @@ -74,6 +76,7 @@ def test_1(model): # Ai function test 2 def test_2(model): + """Generates a random password of given length with or without special characters.""" function_string = "def random_password_generator(length: int, special_chars: bool) -> str:" args = ["12", "True"] description_string = """Generates a random password of given length with or without special characters.""" @@ -88,6 +91,7 @@ def test_2(model): # Ai function test 3 def test_3(model): + """Calculates the area of a triangle given its base and height.""" function_string = "def calculate_area_of_triangle(base: float, height: float) -> float:" args = ["15", "6.5"] description_string = """Calculates the area of a triangle given its base and height.""" @@ -110,6 +114,7 @@ def test_3(model): # Ai function test 4 def test_4(model): + """Finds and returns the nth prime number.""" function_string = "def get_nth_prime_number(n: int) -> int:" args = ["10"] description_string = """Finds and returns the nth prime number.""" @@ -133,6 +138,7 @@ def test_4(model): # Ai function test 5 def test_5(model): + """Encrypts the given text using a simple character substitution based on the provided key.""" function_string = "def encrypt_text(text: str, key: str) -> str:" args = ["'Hello, World!'", "'abc123'"] description_string = """Encrypts the given text using a simple character substitution based on the provided key.""" @@ -147,6 +153,7 @@ def test_5(model): # Ai function test 6 def test_6(model): + """Finds and returns a list of missing numbers in a given sorted list.""" function_string = "def find_missing_numbers_in_list(numbers: list[int]) -> list[int]:" args = ["[3, 5, 8, 15, 16]"] description_string = """Finds and returns a list of missing numbers in a given sorted list.""" From 71d5e76e453655e8cbaaf120352905d63325565d Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sat, 22 Apr 2023 20:22:19 +0200 Subject: [PATCH 6/6] Rename ai_fuctions file --- ai/__init__.py | 2 +- ai/{ai_functions.py => functions.py} | 0 tests/test_ai_function.py | 14 +++++++------- 3 files changed, 8 insertions(+), 8 deletions(-) rename ai/{ai_functions.py => functions.py} (100%) diff --git a/ai/__init__.py b/ai/__init__.py index b705176..4f7432c 100644 --- a/ai/__init__.py +++ b/ai/__init__.py @@ -1 +1 @@ -from .ai_functions import ai_function \ No newline at end of file +from .functions import ai_function \ No newline at end of file diff --git a/ai/ai_functions.py b/ai/functions.py similarity index 100% rename from ai/ai_functions.py rename to ai/functions.py diff --git a/tests/test_ai_function.py b/tests/test_ai_function.py index 25d05a9..a056de8 100644 --- a/tests/test_ai_function.py +++ b/tests/test_ai_function.py @@ -1,7 +1,7 @@ import ast import json import time -import ai.ai_functions as ai_functions +import ai.functions as functions import pytest import openai from dotenv import load_dotenv @@ -52,7 +52,7 @@ def test_1(model): description_string = """Generates n examples of fake data representing people, each with a name and an age.""" - result_string = ai_functions.ai_function(function_string, args, description_string, model) + result_string = functions.ai_function(function_string, args, description_string, model) print(f"Output: {result_string}") # Assert the result can be parsed as is a list of dictionaries @@ -81,7 +81,7 @@ def test_2(model): args = ["12", "True"] description_string = """Generates a random password of given length with or without special characters.""" - result_string = ai_functions.ai_function(function_string, args, description_string, model) + result_string = functions.ai_function(function_string, args, description_string, model) print(f"Output: {result_string}") @@ -96,7 +96,7 @@ def test_3(model): args = ["15", "6.5"] description_string = """Calculates the area of a triangle given its base and height.""" - result_string = ai_functions.ai_function(function_string, args, description_string, model) + result_string = functions.ai_function(function_string, args, description_string, model) print(f"Output: {result_string}") # Assert the result can be parsed as a float @@ -119,7 +119,7 @@ def test_4(model): args = ["10"] description_string = """Finds and returns the nth prime number.""" - result_string = ai_functions.ai_function(function_string, args, description_string, model) + result_string = functions.ai_function(function_string, args, description_string, model) print(f"Output: {result_string}") @@ -143,7 +143,7 @@ def test_5(model): args = ["'Hello, World!'", "'abc123'"] description_string = """Encrypts the given text using a simple character substitution based on the provided key.""" - result_string = ai_functions.ai_function(function_string, args, description_string, model) + result_string = functions.ai_function(function_string, args, description_string, model) print(f"Output: {result_string}") @@ -158,7 +158,7 @@ def test_6(model): args = ["[3, 5, 8, 15, 16]"] description_string = """Finds and returns a list of missing numbers in a given sorted list.""" - result_string = ai_functions.ai_function(function_string, args, description_string, model) + result_string = functions.ai_function(function_string, args, description_string, model) print(f"Output: {result_string}")