From d5cd0d1fbad4609746e20899d372bcbc40bf3eba Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Fri, 28 Apr 2023 18:27:40 +0000 Subject: [PATCH 1/7] Update remove_color_codes to handle non-string input The `remove_color_codes` function now accepts any type of input that can be cast to a string. Previously, it was only accepting string input and not casting non-string types to string which was causing errors in some cases. The changes were made to both logs.py and its corresponding test file. --- autogpt/logs.py | 2 +- tests/test_logs.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/autogpt/logs.py b/autogpt/logs.py index fc529f25b327..c5e602af941b 100644 --- a/autogpt/logs.py +++ b/autogpt/logs.py @@ -199,7 +199,7 @@ def format(self, record: LogRecord) -> str: def remove_color_codes(s: str) -> str: ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") - return ansi_escape.sub("", s) + return ansi_escape.sub("", str(s)) logger = Logger() diff --git a/tests/test_logs.py b/tests/test_logs.py index 0e8660b6cf3e..9c0ad5e129d4 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -22,6 +22,10 @@ "\x1B[1m\x1B[31mError:\x1B[0m\x1B[31m file not found", "Error: file not found", ), + ( + {"I": "am a dict"}, + str({"I": "am a dict"}), + ), ], ) def test_remove_color_codes(raw_text, clean_text): From 40c3f106c4fb832d7ee46cb2e74fec731bff4ecd Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Fri, 28 Apr 2023 20:30:14 +0000 Subject: [PATCH 2/7] Refactor AIConfig to Sanitize Input for Goal Parameters Details: - Modified `ai_config.py` to correctly handle and sanitize user input for AI goals and convert them to formatted strings, to fix an issue where some specially formatted ai_settings.yaml files were causing goals to load as list[dict] - `test_ai_config.py` includes a test for the `sanitize_input` function in `AIConfig` class. - Removed unnecessary tests from `test_logs.py` --- autogpt/config/ai_config.py | 21 ++++++++++++++++++++- autogpt/logs.py | 2 +- tests/test_ai_config.py | 30 ++++++++++++++++++++++++++++++ tests/test_logs.py | 4 ---- 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/test_ai_config.py diff --git a/autogpt/config/ai_config.py b/autogpt/config/ai_config.py index d662429f2d8a..39d8cd722354 100644 --- a/autogpt/config/ai_config.py +++ b/autogpt/config/ai_config.py @@ -7,7 +7,7 @@ import os import platform from pathlib import Path -from typing import Optional, Type +from typing import Any, Optional, Type import distro import yaml @@ -79,11 +79,30 @@ def load(config_file: str = SAVE_FILE) -> "AIConfig": ai_name = config_params.get("ai_name", "") ai_role = config_params.get("ai_role", "") + ai_goals = config_params.get("ai_role", "") ai_goals = config_params.get("ai_goals", []) + ai_goals = list(map(AIConfig.sanitize_input, ai_goals)) api_budget = config_params.get("api_budget", 0.0) # type: Type[AIConfig] return AIConfig(ai_name, ai_role, ai_goals, api_budget) + @staticmethod + def sanitize_input(input: Any) -> str: + """ + Try to return a string, in the format that the user intended. + + Parameters: + goal: The input to sanitize (can be a string, dict, or other object) + + Returns: + A string + """ + return ( + " ".join(f"{k}: {v}" for k, v in input.items()) + if isinstance(input, dict) + else str(input) + ) + def save(self, config_file: str = SAVE_FILE) -> None: """ Saves the class parameters to the specified file yaml file path as a yaml file. diff --git a/autogpt/logs.py b/autogpt/logs.py index c5e602af941b..fc529f25b327 100644 --- a/autogpt/logs.py +++ b/autogpt/logs.py @@ -199,7 +199,7 @@ def format(self, record: LogRecord) -> str: def remove_color_codes(s: str) -> str: ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") - return ansi_escape.sub("", str(s)) + return ansi_escape.sub("", s) logger = Logger() diff --git a/tests/test_ai_config.py b/tests/test_ai_config.py new file mode 100644 index 000000000000..c3b2e8bf40aa --- /dev/null +++ b/tests/test_ai_config.py @@ -0,0 +1,30 @@ +from autogpt.config.ai_config import AIConfig + +""" +Test cases for the AIConfig class, which handles loads the AI configuration +settings from a YAML file. +""" + + +def test_goals_are_always_lists_of_strings(tmp_path): + """ + Test if the goals attribute is always a list of strings. + """ + yaml_content = """ +ai_goals: +- Goal 1: Make a sandwich +- Goal 2, Eat the sandwich +- Goal 3 - Go to sleep +ai_role: A hungry AI +ai_name: McFamished +""" + + config_file = tmp_path / "ai_settings.yaml" + config_file.write_text(yaml_content) + + ai_config = AIConfig.load(config_file) + + assert len(ai_config.ai_goals) == 3 + assert ai_config.ai_goals[0] == "Goal 1: Make a sandwich" + assert ai_config.ai_goals[1] == "Goal 2, Eat the sandwich" + assert ai_config.ai_goals[2] == "Goal 3 - Go to sleep" diff --git a/tests/test_logs.py b/tests/test_logs.py index 9c0ad5e129d4..0e8660b6cf3e 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -22,10 +22,6 @@ "\x1B[1m\x1B[31mError:\x1B[0m\x1B[31m file not found", "Error: file not found", ), - ( - {"I": "am a dict"}, - str({"I": "am a dict"}), - ), ], ) def test_remove_color_codes(raw_text, clean_text): From 79eb5c30ecc3b7b2cac56b4fecf03c1ce7fca8bd Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Fri, 28 Apr 2023 21:30:33 +0000 Subject: [PATCH 3/7] Update for readabiity --- autogpt/config/ai_config.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/autogpt/config/ai_config.py b/autogpt/config/ai_config.py index 39d8cd722354..6f2019cf3765 100644 --- a/autogpt/config/ai_config.py +++ b/autogpt/config/ai_config.py @@ -80,28 +80,29 @@ def load(config_file: str = SAVE_FILE) -> "AIConfig": ai_name = config_params.get("ai_name", "") ai_role = config_params.get("ai_role", "") ai_goals = config_params.get("ai_role", "") - ai_goals = config_params.get("ai_goals", []) - ai_goals = list(map(AIConfig.sanitize_input, ai_goals)) + ai_goals = AIConfig.coerce_goals_to_str(config_params.get("ai_goals", [])) api_budget = config_params.get("api_budget", 0.0) # type: Type[AIConfig] return AIConfig(ai_name, ai_role, ai_goals, api_budget) @staticmethod - def sanitize_input(input: Any) -> str: + def coerce_goals_to_str(goals: list[Any]) -> list[str]: """ - Try to return a string, in the format that the user intended. + Return goals as list of strings, in the format that the user intended, + fixing some cases where the goals are loaded from yaml as a list of dicts. Parameters: - goal: The input to sanitize (can be a string, dict, or other object) + goals: The goals to sanitize (can be a string, dict, or other object) Returns: - A string + A list of strings, where each string is a goal. """ - return ( - " ".join(f"{k}: {v}" for k, v in input.items()) - if isinstance(input, dict) - else str(input) + coerce_goal_to_str = ( + lambda goal: " ".join(f"{k}: {v}" for k, v in goal.items()) + if isinstance(goal, dict) + else str(goal) ) + return list(map(coerce_goal_to_str, goals)) def save(self, config_file: str = SAVE_FILE) -> None: """ From 7eef912855b631817f7df671cd587ecfc012a959 Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Fri, 28 Apr 2023 21:30:53 +0000 Subject: [PATCH 4/7] Update for readabiity --- autogpt/config/ai_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autogpt/config/ai_config.py b/autogpt/config/ai_config.py index 6f2019cf3765..9ce28cc1f511 100644 --- a/autogpt/config/ai_config.py +++ b/autogpt/config/ai_config.py @@ -97,12 +97,12 @@ def coerce_goals_to_str(goals: list[Any]) -> list[str]: Returns: A list of strings, where each string is a goal. """ - coerce_goal_to_str = ( + to_str = ( lambda goal: " ".join(f"{k}: {v}" for k, v in goal.items()) if isinstance(goal, dict) else str(goal) ) - return list(map(coerce_goal_to_str, goals)) + return list(map(to_str, goals)) def save(self, config_file: str = SAVE_FILE) -> None: """ From 6716ff8ad550ef836e2d9902f54bb7245b0cd2f2 Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Sat, 29 Apr 2023 02:39:42 +0000 Subject: [PATCH 5/7] Updates for conciceness --- autogpt/config/ai_config.py | 27 ++++++--------------------- tests/test_ai_config.py | 1 - 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/autogpt/config/ai_config.py b/autogpt/config/ai_config.py index 9ce28cc1f511..f43e72684811 100644 --- a/autogpt/config/ai_config.py +++ b/autogpt/config/ai_config.py @@ -79,31 +79,16 @@ def load(config_file: str = SAVE_FILE) -> "AIConfig": ai_name = config_params.get("ai_name", "") ai_role = config_params.get("ai_role", "") - ai_goals = config_params.get("ai_role", "") - ai_goals = AIConfig.coerce_goals_to_str(config_params.get("ai_goals", [])) + ai_goals = [ + str(goal).strip("{}").replace("'", "") + if isinstance(goal, dict) + else str(goal) + for goal in config_params.get("ai_goals", []) + ] api_budget = config_params.get("api_budget", 0.0) # type: Type[AIConfig] return AIConfig(ai_name, ai_role, ai_goals, api_budget) - @staticmethod - def coerce_goals_to_str(goals: list[Any]) -> list[str]: - """ - Return goals as list of strings, in the format that the user intended, - fixing some cases where the goals are loaded from yaml as a list of dicts. - - Parameters: - goals: The goals to sanitize (can be a string, dict, or other object) - - Returns: - A list of strings, where each string is a goal. - """ - to_str = ( - lambda goal: " ".join(f"{k}: {v}" for k, v in goal.items()) - if isinstance(goal, dict) - else str(goal) - ) - return list(map(to_str, goals)) - def save(self, config_file: str = SAVE_FILE) -> None: """ Saves the class parameters to the specified file yaml file path as a yaml file. diff --git a/tests/test_ai_config.py b/tests/test_ai_config.py index c3b2e8bf40aa..87b7ef37f416 100644 --- a/tests/test_ai_config.py +++ b/tests/test_ai_config.py @@ -18,7 +18,6 @@ def test_goals_are_always_lists_of_strings(tmp_path): ai_role: A hungry AI ai_name: McFamished """ - config_file = tmp_path / "ai_settings.yaml" config_file.write_text(yaml_content) From a52d62f47b510e3a42163b1ae2cd0edf1f7dfc3c Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Sat, 29 Apr 2023 17:03:28 +0000 Subject: [PATCH 6/7] Updated tests to confirm AIConfig saves goals as strings --- autogpt/config/ai_config.py | 2 +- tests/test_ai_config.py | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/autogpt/config/ai_config.py b/autogpt/config/ai_config.py index f43e72684811..88acbfe6fb72 100644 --- a/autogpt/config/ai_config.py +++ b/autogpt/config/ai_config.py @@ -80,7 +80,7 @@ def load(config_file: str = SAVE_FILE) -> "AIConfig": ai_name = config_params.get("ai_name", "") ai_role = config_params.get("ai_role", "") ai_goals = [ - str(goal).strip("{}").replace("'", "") + str(goal).strip("{}").replace("'", "").replace('"', "") if isinstance(goal, dict) else str(goal) for goal in config_params.get("ai_goals", []) diff --git a/tests/test_ai_config.py b/tests/test_ai_config.py index 87b7ef37f416..0384c2b838c4 100644 --- a/tests/test_ai_config.py +++ b/tests/test_ai_config.py @@ -7,23 +7,39 @@ def test_goals_are_always_lists_of_strings(tmp_path): - """ - Test if the goals attribute is always a list of strings. - """ + """Test if the goals attribute is always a list of strings.""" + yaml_content = """ ai_goals: - Goal 1: Make a sandwich - Goal 2, Eat the sandwich - Goal 3 - Go to sleep -ai_role: A hungry AI +- "Goal 4: Wake up" ai_name: McFamished +ai_role: A hungry AI +api_budget: 0.0 """ config_file = tmp_path / "ai_settings.yaml" config_file.write_text(yaml_content) ai_config = AIConfig.load(config_file) - assert len(ai_config.ai_goals) == 3 + assert len(ai_config.ai_goals) == 4 assert ai_config.ai_goals[0] == "Goal 1: Make a sandwich" assert ai_config.ai_goals[1] == "Goal 2, Eat the sandwich" assert ai_config.ai_goals[2] == "Goal 3 - Go to sleep" + assert ai_config.ai_goals[3] == "Goal 4: Wake up" + + config_file.write_text("") + ai_config.save(config_file) + + yaml_content2 = """ai_goals: +- 'Goal 1: Make a sandwich' +- Goal 2, Eat the sandwich +- Goal 3 - Go to sleep +- 'Goal 4: Wake up' +ai_name: McFamished +ai_role: A hungry AI +api_budget: 0.0 +""" + assert config_file.read_text() == yaml_content2 From 1b0efeb718e326e7d907fbfda18eed3bd48f1cbd Mon Sep 17 00:00:00 2001 From: Luke Kyohere Date: Sun, 30 Apr 2023 02:43:01 +0000 Subject: [PATCH 7/7] FIxed trailing space at end of line --- tests/test_ai_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_ai_config.py b/tests/test_ai_config.py index 0384c2b838c4..a9fcdad68e90 100644 --- a/tests/test_ai_config.py +++ b/tests/test_ai_config.py @@ -9,7 +9,7 @@ def test_goals_are_always_lists_of_strings(tmp_path): """Test if the goals attribute is always a list of strings.""" - yaml_content = """ + yaml_content = """ ai_goals: - Goal 1: Make a sandwich - Goal 2, Eat the sandwich