diff --git a/autogpt/llm_utils.py b/autogpt/llm_utils.py index da6cb979..ba7521a4 100644 --- a/autogpt/llm_utils.py +++ b/autogpt/llm_utils.py @@ -154,6 +154,13 @@ def create_chat_completion( return resp +def get_ada_embedding(text): + text = text.replace("\n", " ") + return api_manager.embedding_create( + text_list=[text], model="text-embedding-ada-002" + ) + + def create_embedding_with_ada(text) -> list: """Create an embedding with text-ada-002 using the OpenAI SDK""" num_retries = 10 diff --git a/autogpt/memory/base.py b/autogpt/memory/base.py index b69f795c..b6252464 100644 --- a/autogpt/memory/base.py +++ b/autogpt/memory/base.py @@ -1,21 +1,11 @@ """Base class for memory providers.""" import abc -import openai - -from autogpt.api_manager import api_manager from autogpt.config import AbstractSingleton, Config cfg = Config() -def get_ada_embedding(text): - text = text.replace("\n", " ") - return api_manager.embedding_create( - text_list=[text], model="text-embedding-ada-002" - ) - - class MemoryProviderSingleton(AbstractSingleton): @abc.abstractmethod def add(self, data): diff --git a/autogpt/memory/milvus.py b/autogpt/memory/milvus.py index 1849a9e6..085f50b4 100644 --- a/autogpt/memory/milvus.py +++ b/autogpt/memory/milvus.py @@ -4,7 +4,8 @@ import re from pymilvus import Collection, CollectionSchema, DataType, FieldSchema, connections from autogpt.config import Config -from autogpt.memory.base import MemoryProviderSingleton, get_ada_embedding +from autogpt.llm_utils import get_ada_embedding +from autogpt.memory.base import MemoryProviderSingleton class MilvusMemory(MemoryProviderSingleton): diff --git a/autogpt/memory/weaviate.py b/autogpt/memory/weaviate.py index 0225ae04..fbebbfd7 100644 --- a/autogpt/memory/weaviate.py +++ b/autogpt/memory/weaviate.py @@ -1,12 +1,10 @@ -import uuid - import weaviate from weaviate import Client from weaviate.embedded import EmbeddedOptions from weaviate.util import generate_uuid5 -from autogpt.config import Config -from autogpt.memory.base import MemoryProviderSingleton, get_ada_embedding +from autogpt.llm_utils import get_ada_embedding +from autogpt.memory.base import MemoryProviderSingleton def default_schema(weaviate_index): diff --git a/tests/integration/weaviate_memory_tests.py b/tests/integration/weaviate_memory_tests.py index 015eab05..5448b79e 100644 --- a/tests/integration/weaviate_memory_tests.py +++ b/tests/integration/weaviate_memory_tests.py @@ -1,14 +1,11 @@ -import os -import sys import unittest -from unittest import mock from uuid import uuid4 from weaviate import Client from weaviate.util import get_valid_uuid from autogpt.config import Config -from autogpt.memory.base import get_ada_embedding +from autogpt.llm_utils import get_ada_embedding from autogpt.memory.weaviate import WeaviateMemory diff --git a/tests/test_permanent_memory.py b/tests/test_permanent_memory.py new file mode 100644 index 00000000..943c61fe --- /dev/null +++ b/tests/test_permanent_memory.py @@ -0,0 +1,59 @@ +import os +import unittest + +from autogpt.permanent_memory.sqlite3_store import MemoryDB + + +class TestMemoryDB(unittest.TestCase): + def setUp(self): + self.db_filename = "test_db.sqlite3" + self.db = MemoryDB(self.db_filename) + + def tearDown(self): + self.db.quit() + os.remove(self.db_filename) + + def test_overwrite_and_get_session(self): + self.db.insert("The quick brown fox jumps over the lazy dog") + self.db.insert("The five boxing wizards jump quickly") + + # Overwrite the second text + self.db.overwrite(1, "The slow elephant walks carefully") + + # Get the session and verify the texts + session = self.db.get_session() + self.assertEqual(len(session), 2) + self.assertIn("The quick brown fox jumps over the lazy dog", session) + self.assertIn("The slow elephant walks carefully", session) + + # Overwrite the first text + self.db.overwrite(0, "The lazy dog jumps over the quick brown fox") + + # Get the session and verify the texts + session = self.db.get_session() + self.assertEqual(len(session), 2) + self.assertIn("The lazy dog jumps over the quick brown fox", session) + self.assertIn("The slow elephant walks carefully", session) + + def test_delete_memory(self): + self.db.insert("The quick brown fox jumps over the lazy dog") + self.db.insert("The five boxing wizards jump quickly") + + # Delete the first text + self.db.delete_memory(0) + + # Get the session and verify the remaining text + session = self.db.get_session() + self.assertEqual(len(session), 1) + self.assertIn("The five boxing wizards jump quickly", session) + + # Delete the remaining text + self.db.delete_memory(1) + + # Get the session and verify that it's empty + session = self.db.get_session() + self.assertEqual(len(session), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/test_json_utils_llm.py b/tests/unit/test_json_utils_llm.py new file mode 100644 index 00000000..b8c9518d --- /dev/null +++ b/tests/unit/test_json_utils_llm.py @@ -0,0 +1,117 @@ +# Generated by CodiumAI +import pytest +from loguru import logger + +from autogpt.json_utils.json_fix_llm import ( + fix_and_parse_json, + fix_json_using_multiple_techniques, +) +from tests.utils import requires_api_key + +""" +Code Analysis + +Objective: +- The objective of the function is to fix a given JSON string to make it parseable and fully compliant with two techniques. + +Inputs: +- The function takes in a string called 'assistant_reply', which is the JSON string to be fixed. + +Flow: +- The function first calls the 'fix_and_parse_json' function to parse and print the Assistant response. +- If the parsed JSON is an empty dictionary, the function calls the 'attempt_to_fix_json_by_finding_outermost_brackets' function to fix the JSON string. +- If the parsed JSON is not an empty dictionary, the function returns the parsed JSON. +- If the parsed JSON is an empty dictionary and cannot be fixed, the function logs an error and returns an empty dictionary. + +Outputs: +- The main output of the function is a dictionary containing the fixed JSON string. + +Additional aspects: +- The function uses two techniques to fix the JSON string: parsing and finding outermost brackets. +- The function logs an error if the JSON string cannot be fixed and returns an empty dictionary. +- The function uses the 'CFG' object to determine whether to speak the error message or not. +""" + + +class TestFixJsonUsingMultipleTechniques: + # Tests that the function successfully fixes and parses a JSON string that is already compliant with both techniques. + def test_fix_and_parse_json_happy_path(self): + # Happy path test case where the JSON string is already compliant with both techniques + json_string = '{"text": "Hello world", "confidence": 0.9}' + expected_output = {"text": "Hello world", "confidence": 0.9} + assert fix_json_using_multiple_techniques(json_string) == expected_output + + # Tests that the function successfully fixes and parses a JSON string that contains only whitespace characters. + # @requires_api_key("OPEN_API_KEY") + def test_fix_and_parse_json_whitespace(self, mocker): + # Happy path test case where the JSON string contains only whitespace characters + json_string = " \n\t " + + # mock try_ai_fix to avoid calling the AI model: + mocker.patch("autogpt.json_utils.json_fix_llm.try_ai_fix", return_value={}) + + expected_output = {} + assert fix_json_using_multiple_techniques(json_string) == expected_output + + # Tests that the function successfully converts a string with arrays to an array + def test_fix_and_parse_json_array(self): + # Happy path test case where the JSON string contains an array of string + json_string = '[ "Add type hints", "Move docstrings", "Consider using" ]' + expected_output = ["Add type hints", "Move docstrings", "Consider using"] + assert fix_json_using_multiple_techniques(json_string) == expected_output + + # Tests that the function returns an empty dictionary when the JSON string is not parseable and cannot be fixed using either technique. + # @requires_api_key("OPEN_API_KEY") + def test_fix_and_parse_json_can_not(self, mocker): + # Edge case test case where the JSON string is not parseable and cannot be fixed using either technique + json_string = "This is not a JSON string" + + # mock try_ai_fix to avoid calling the AI model: + mocker.patch("autogpt.json_utils.json_fix_llm.try_ai_fix", return_value={}) + + expected_output = {} + + # Use the actual function name in the test + result = fix_json_using_multiple_techniques(json_string) + + assert result == expected_output + + # Tests that the function returns an empty dictionary when the JSON string is empty. + # @requires_api_key("OPEN_API_KEY") + def test_fix_and_parse_json_empty_string(self, mocker): + # Arrange + json_string = "" + + # Act + # mock try_ai_fix to avoid calling the AI model: + mocker.patch("autogpt.json_utils.json_fix_llm.try_ai_fix", return_value={}) + + result = fix_and_parse_json(json_string) + + # Assert + assert result == {} + + # Tests that the function successfully fixes and parses a JSON string that contains escape characters. + def test_fix_and_parse_json_escape_characters(self): + # Arrange + json_string = '{"text": "This is a \\"test\\" string."}' + + # Act + result = fix_json_using_multiple_techniques(json_string) + + # Assert + assert result == {"text": 'This is a "test" string.'} + + # Tests that the function successfully fixes and parses a JSON string that contains nested objects or arrays. + def test_fix_and_parse_json_nested_objects(self): + # Arrange + json_string = '{"person": {"name": "John", "age": 30}, "hobbies": ["reading", "swimming"]}' + + # Act + result = fix_json_using_multiple_techniques(json_string) + + # Assert + assert result == { + "person": {"name": "John", "age": 30}, + "hobbies": ["reading", "swimming"], + }