From ac16840bc77c35e2483b6cda8bc02b67e25ccc94 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 14:18:57 -0700 Subject: [PATCH 1/7] fix: remove import --- tests/test_agent_function_update.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_agent_function_update.py b/tests/test_agent_function_update.py index 524966e9..26914f1a 100644 --- a/tests/test_agent_function_update.py +++ b/tests/test_agent_function_update.py @@ -4,12 +4,11 @@ import uuid import pytest -from memgpt import constants, create_client +from memgpt import create_client from memgpt.functions.functions import USER_FUNCTIONS_DIR from memgpt.schemas.message import Message from memgpt.settings import settings from memgpt.utils import assistant_function_to_tool, json_dumps, json_loads -from tests.mock_factory.models import MockUserFactory from tests.utils import create_config, wipe_config From e384635ce19f0dd239263967a815e986768a551f Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 14:20:43 -0700 Subject: [PATCH 2/7] fix: fix test_json_parsers.py --- tests/test_json_parsers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_json_parsers.py b/tests/test_json_parsers.py index d04aa5d6..bd68a1e3 100644 --- a/tests/test_json_parsers.py +++ b/tests/test_json_parsers.py @@ -1,7 +1,5 @@ -from mempgt.utils import json_loads - import memgpt.local_llm.json_parser as json_parser -from memgpt.constants import json +from memgpt.utils import json_loads EXAMPLE_ESCAPED_UNDERSCORES = """{ "function":"send\_message", From 99e2ae5c86d74c7febd617721b830330cdd77758 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 14:31:02 -0700 Subject: [PATCH 3/7] fix: remove test for unmerged function adding code --- tests/test_agent_function_update.py | 108 +++++++++++++--------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/tests/test_agent_function_update.py b/tests/test_agent_function_update.py index 26914f1a..5c545f0d 100644 --- a/tests/test_agent_function_update.py +++ b/tests/test_agent_function_update.py @@ -1,14 +1,12 @@ import inspect import os -import uuid import pytest from memgpt import create_client from memgpt.functions.functions import USER_FUNCTIONS_DIR from memgpt.schemas.message import Message -from memgpt.settings import settings -from memgpt.utils import assistant_function_to_tool, json_dumps, json_loads +from memgpt.utils import assistant_function_to_tool, json_dumps from tests.utils import create_config, wipe_config @@ -34,16 +32,9 @@ def agent(): # create memgpt client client = create_client() - # ensure user exists - user_id = uuid.UUID(TEST_MEMGPT_CONFIG.anon_clientid) - if not client.server.get_user(user_id=user_id): - client.server.create_user({"id": user_id}) + agent_state = client.create_agent() - agent_state = client.create_agent( - preset=settings.preset, - ) - - return client.server._get_or_load_agent(user_id=user_id, agent_id=agent_state.id) + return client.server._get_or_load_agent(agent_id=agent_state.id) @pytest.fixture(scope="module") @@ -58,7 +49,7 @@ def ai_function_call(): **assistant_function_to_tool( { "role": "assistant", - "content": "I will now call hello world", + "text": "I will now call hello world", # TODO: change to `content` once `Message` is updated "function_call": { "name": "hello_world", "arguments": json_dumps({}), @@ -68,53 +59,54 @@ def ai_function_call(): ) -def test_add_function_happy(agent, hello_world_function, ai_function_call): - agent.add_function("hello_world") - - assert "hello_world" in [f_schema["name"] for f_schema in agent.functions] - assert "hello_world" in agent.functions_python.keys() - - msgs, heartbeat_req, function_failed = agent._handle_ai_response(ai_function_call) - content = json_loads(msgs[-1].to_openai_dict()["content"]) - assert content["message"] == "hello, world!" - assert content["status"] == "OK" - assert not function_failed +# TODO: add back once implementation completed +# def test_add_function_happy(agent, hello_world_function, ai_function_call): +# agent.add_function("hello_world") +# +# assert "hello_world" in [f_schema["name"] for f_schema in agent.functions] +# assert "hello_world" in agent.functions_python.keys() +# +# msgs, heartbeat_req, function_failed = agent._handle_ai_response(ai_function_call) +# content = json_loads(msgs[-1].to_openai_dict()["content"]) +# assert content["message"] == "hello, world!" +# assert content["status"] == "OK" +# assert not function_failed -def test_add_function_already_loaded(agent, hello_world_function): - agent.add_function("hello_world") - # no exception for duplicate loading - agent.add_function("hello_world") - - -def test_add_function_not_exist(agent): - # pytest assert exception - with pytest.raises(ValueError): - agent.add_function("non_existent") - - -def test_remove_function_happy(agent, hello_world_function): - agent.add_function("hello_world") - - # ensure function is loaded - assert "hello_world" in [f_schema["name"] for f_schema in agent.functions] - assert "hello_world" in agent.functions_python.keys() - - agent.remove_function("hello_world") - - assert "hello_world" not in [f_schema["name"] for f_schema in agent.functions] - assert "hello_world" not in agent.functions_python.keys() - - -def test_remove_function_not_exist(agent): - # do not raise error - agent.remove_function("non_existent") - - -def test_remove_base_function_fails(agent): - with pytest.raises(ValueError): - agent.remove_function("send_message") - +# def test_add_function_already_loaded(agent, hello_world_function): +# agent.add_function("hello_world") +# # no exception for duplicate loading +# agent.add_function("hello_world") +# +# +# def test_add_function_not_exist(agent): +# # pytest assert exception +# with pytest.raises(ValueError): +# agent.add_function("non_existent") +# +# +# def test_remove_function_happy(agent, hello_world_function): +# agent.add_function("hello_world") +# +# # ensure function is loaded +# assert "hello_world" in [f_schema["name"] for f_schema in agent.functions] +# assert "hello_world" in agent.functions_python.keys() +# +# agent.remove_function("hello_world") +# +# assert "hello_world" not in [f_schema["name"] for f_schema in agent.functions] +# assert "hello_world" not in agent.functions_python.keys() +# +# +# def test_remove_function_not_exist(agent): +# # do not raise error +# agent.remove_function("non_existent") +# +# +# def test_remove_base_function_fails(agent): +# with pytest.raises(ValueError): +# agent.remove_function("send_message") +# if __name__ == "__main__": pytest.main(["-vv", os.path.abspath(__file__)]) From cc039e0c5518aef1896a67b8beb220e69ffac637 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 14:40:24 -0700 Subject: [PATCH 4/7] fix: add import to function sets --- memgpt/functions/function_sets/base.py | 2 +- memgpt/functions/function_sets/extras.py | 2 +- memgpt/server/rest_api/utils.py | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/memgpt/functions/function_sets/base.py b/memgpt/functions/function_sets/base.py index 39c43a92..c44da324 100644 --- a/memgpt/functions/function_sets/base.py +++ b/memgpt/functions/function_sets/base.py @@ -1,10 +1,10 @@ import datetime -import json import math from typing import Optional from memgpt.agent import Agent from memgpt.constants import MAX_PAUSE_HEARTBEATS, RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE +from memgpt.utils import json_dumps ### Functions / tools the agent can use # All functions should return a response string (or None) diff --git a/memgpt/functions/function_sets/extras.py b/memgpt/functions/function_sets/extras.py index 007f346b..aa083ec3 100644 --- a/memgpt/functions/function_sets/extras.py +++ b/memgpt/functions/function_sets/extras.py @@ -1,4 +1,3 @@ -import json import os import uuid from typing import Optional @@ -11,6 +10,7 @@ from memgpt.constants import ( ) from memgpt.llm_api.llm_api_tools import create from memgpt.schemas.message import Message +from memgpt.utils import json_dumps, json_loads def message_chatgpt(self, message: str): diff --git a/memgpt/server/rest_api/utils.py b/memgpt/server/rest_api/utils.py index 79bfcb16..dc448a17 100644 --- a/memgpt/server/rest_api/utils.py +++ b/memgpt/server/rest_api/utils.py @@ -1,14 +1,11 @@ -import json import traceback from enum import Enum from typing import AsyncGenerator, Union from pydantic import BaseModel -from memgpt.orm.user import User -from memgpt.orm.utilities import get_db_session -from memgpt.server.rest_api.interface import StreamingServerInterface -from memgpt.server.server import SyncServer +# from memgpt.orm.user import User +# from memgpt.orm.utilities import get_db_session from memgpt.utils import json_dumps SSE_PREFIX = "data: " From 4d4aadc55de362cee4f1645067e61a5de38bc0be Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 14:45:33 -0700 Subject: [PATCH 5/7] fix: import Generator --- memgpt/server/rest_api/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memgpt/server/rest_api/utils.py b/memgpt/server/rest_api/utils.py index dc448a17..95616576 100644 --- a/memgpt/server/rest_api/utils.py +++ b/memgpt/server/rest_api/utils.py @@ -1,6 +1,6 @@ import traceback from enum import Enum -from typing import AsyncGenerator, Union +from typing import AsyncGenerator, Generator, Union from pydantic import BaseModel From e24a48816c891739802264d6df9961cffd4b799b Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 15:01:49 -0700 Subject: [PATCH 6/7] remove old function schema file --- memgpt/prompts/gpt_functions.py | 271 -------------------------------- tests/test_schema_generator.py | 47 ------ 2 files changed, 318 deletions(-) delete mode 100644 memgpt/prompts/gpt_functions.py diff --git a/memgpt/prompts/gpt_functions.py b/memgpt/prompts/gpt_functions.py deleted file mode 100644 index 9c67f6b6..00000000 --- a/memgpt/prompts/gpt_functions.py +++ /dev/null @@ -1,271 +0,0 @@ -from ..constants import MAX_PAUSE_HEARTBEATS - -request_heartbeat = { - "request_heartbeat": { - "type": "boolean", - "description": "Request an immediate heartbeat after function execution. Set to 'true' if you want to send a follow-up message or run a follow-up function.", - } -} - -# FUNCTIONS_PROMPT_MULTISTEP_NO_HEARTBEATS = FUNCTIONS_PROMPT_MULTISTEP[:-1] -FUNCTIONS_CHAINING = { - "send_message": { - "name": "send_message", - "description": "Sends a message to the human user.", - "parameters": { - "type": "object", - "properties": { - # https://json-schema.org/understanding-json-schema/reference/array.html - "message": { - "type": "string", - "description": "Message contents. All unicode (including emojis) are supported.", - }, - }, - "required": ["message"], - }, - }, - "pause_heartbeats": { - "name": "pause_heartbeats", - "description": "Temporarily ignore timed heartbeats. You may still receive messages from manual heartbeats and other events.", - "parameters": { - "type": "object", - "properties": { - # https://json-schema.org/understanding-json-schema/reference/array.html - "minutes": { - "type": "integer", - "description": f"Number of minutes to ignore heartbeats for. Max value of {MAX_PAUSE_HEARTBEATS} minutes ({MAX_PAUSE_HEARTBEATS//60} hours).", - }, - }, - "required": ["minutes"], - }, - }, - "message_chatgpt": { - "name": "message_chatgpt", - "description": "Send a message to a more basic AI, ChatGPT. A useful resource for asking questions. ChatGPT does not retain memory of previous interactions.", - "parameters": { - "type": "object", - "properties": { - # https://json-schema.org/understanding-json-schema/reference/array.html - "message": { - "type": "string", - "description": "Message to send ChatGPT. Phrase your message as a full English sentence.", - } - }.update(request_heartbeat), - "required": ["message", "request_heartbeat"], - }, - }, - "core_memory_append": { - "name": "core_memory_append", - "description": "Append to the contents of core memory.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Section of the memory to be edited (persona or human).", - }, - "content": { - "type": "string", - "description": "Content to write to the memory. All unicode (including emojis) are supported.", - }, - }.update(request_heartbeat), - "required": ["name", "content", "request_heartbeat"], - }, - }, - "core_memory_replace": { - "name": "core_memory_replace", - "description": "Replace the contents of core memory. To delete memories, use an empty string for new_content.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Section of the memory to be edited (persona or human).", - }, - "old_content": { - "type": "string", - "description": "String to replace. Must be an exact match.", - }, - "new_content": { - "type": "string", - "description": "Content to write to the memory. All unicode (including emojis) are supported.", - }, - }.update(request_heartbeat), - "required": ["name", "old_content", "new_content", "request_heartbeat"], - }, - }, - "recall_memory_search": { - "name": "recall_memory_search", - "description": "Search prior conversation history using a string.", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "String to search for.", - }, - "page": { - "type": "integer", - "description": "Allows you to page through results. Only use on a follow-up query. Defaults to 0 (first page).", - }, - }.update(request_heartbeat), - "required": ["query", "page", "request_heartbeat"], - }, - }, - "conversation_search": { - "name": "conversation_search", - "description": "Search prior conversation history using case-insensitive string matching.", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "String to search for.", - }, - "page": { - "type": "integer", - "description": "Allows you to page through results. Only use on a follow-up query. Defaults to 0 (first page).", - }, - }.update(request_heartbeat), - "required": ["query", "request_heartbeat"], - }, - }, - "recall_memory_search_date": { - "name": "recall_memory_search_date", - "description": "Search prior conversation history using a date range.", - "parameters": { - "type": "object", - "properties": { - "start_date": { - "type": "string", - "description": "The start of the date range to search, in the format 'YYYY-MM-DD'.", - }, - "end_date": { - "type": "string", - "description": "The end of the date range to search, in the format 'YYYY-MM-DD'.", - }, - "page": { - "type": "integer", - "description": "Allows you to page through results. Only use on a follow-up query. Defaults to 0 (first page).", - }, - }.update(request_heartbeat), - "required": ["start_date", "end_date", "page", "request_heartbeat"], - }, - }, - "conversation_search_date": { - "name": "conversation_search_date", - "description": "Search prior conversation history using a date range.", - "parameters": { - "type": "object", - "properties": { - "start_date": { - "type": "string", - "description": "The start of the date range to search, in the format 'YYYY-MM-DD'.", - }, - "end_date": { - "type": "string", - "description": "The end of the date range to search, in the format 'YYYY-MM-DD'.", - }, - "page": { - "type": "integer", - "description": "Allows you to page through results. Only use on a follow-up query. Defaults to 0 (first page).", - }, - }.update(request_heartbeat), - "required": ["start_date", "end_date", "request_heartbeat"], - }, - }, - "archival_memory_insert": { - "name": "archival_memory_insert", - "description": "Add to archival memory. Make sure to phrase the memory contents such that it can be easily queried later.", - "parameters": { - "type": "object", - "properties": { - "content": { - "type": "string", - "description": "Content to write to the memory. All unicode (including emojis) are supported.", - }, - }.update(request_heartbeat), - "required": ["content", "request_heartbeat"], - }, - }, - "archival_memory_search": { - "name": "archival_memory_search", - "description": "Search archival memory using semantic (embedding-based) search.", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "String to search for.", - }, - "page": { - "type": "integer", - "description": "Allows you to page through results. Only use on a follow-up query. Defaults to 0 (first page).", - }, - }.update(request_heartbeat), - "required": ["query", "request_heartbeat"], - }, - }, - "read_from_text_file": { - "name": "read_from_text_file", - "description": "Read lines from a text file.", - "parameters": { - "type": "object", - "properties": { - "filename": { - "type": "string", - "description": "The name of the file to read.", - }, - "line_start": { - "type": "integer", - "description": "Line to start reading from.", - }, - "num_lines": { - "type": "integer", - "description": "How many lines to read (defaults to 1).", - }, - }.update(request_heartbeat), - "required": ["filename", "line_start", "request_heartbeat"], - }, - }, - "append_to_text_file": { - "name": "append_to_text_file", - "description": "Append to a text file.", - "parameters": { - "type": "object", - "properties": { - "filename": { - "type": "string", - "description": "The name of the file to append to.", - }, - "content": { - "type": "string", - "description": "Content to append to the file.", - }, - }.update(request_heartbeat), - "required": ["filename", "content", "request_heartbeat"], - }, - }, - "http_request": { - "name": "http_request", - "description": "Generates an HTTP request and returns the response.", - "parameters": { - "type": "object", - "properties": { - "method": { - "type": "string", - "description": "The HTTP method (e.g., 'GET', 'POST').", - }, - "url": { - "type": "string", - "description": "The URL for the request.", - }, - "payload_json": { - "type": "string", - "description": "A JSON string representing the request payload.", - }, - }.update(request_heartbeat), - "required": ["method", "url", "request_heartbeat"], - }, - }, -} diff --git a/tests/test_schema_generator.py b/tests/test_schema_generator.py index ac9322b0..e7861c30 100644 --- a/tests/test_schema_generator.py +++ b/tests/test_schema_generator.py @@ -1,9 +1,4 @@ -import inspect - -import memgpt.functions.function_sets.base as base_functions -import memgpt.functions.function_sets.extras as extras_functions from memgpt.functions.schema_generator import generate_schema -from memgpt.prompts.gpt_functions import FUNCTIONS_CHAINING def send_message(self, message: str): @@ -65,45 +60,3 @@ def test_schema_generator(): assert False except: pass - - -def test_schema_generator_with_old_function_set(): - # Try all the base functions first - for attr_name in dir(base_functions): - # Get the attribute - attr = getattr(base_functions, attr_name) - - # Check if it's a callable function and not a built-in or special method - if inspect.isfunction(attr): - # Here, 'func' is each function in base_functions - # You can now call the function or do something with it - print("Function name:", attr) - # Example function call (if the function takes no arguments) - # result = func() - function_name = str(attr_name) - real_schema = FUNCTIONS_CHAINING[function_name] - generated_schema = generate_schema(attr) - print(f"\n\nreference_schema={real_schema}") - print(f"\n\ngenerated_schema={generated_schema}") - assert real_schema == generated_schema - - # Then try all the extras functions - for attr_name in dir(extras_functions): - # Get the attribute - attr = getattr(extras_functions, attr_name) - - # Check if it's a callable function and not a built-in or special method - if inspect.isfunction(attr): - if attr_name == "create": - continue - # Here, 'func' is each function in base_functions - # You can now call the function or do something with it - print("Function name:", attr) - # Example function call (if the function takes no arguments) - # result = func() - function_name = str(attr_name) - real_schema = FUNCTIONS_CHAINING[function_name] - generated_schema = generate_schema(attr) - print(f"\n\nreference_schema={real_schema}") - print(f"\n\ngenerated_schema={generated_schema}") - assert real_schema == generated_schema From 659129075474f66cfe224208e51e25aa3075f4a5 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Mon, 26 Aug 2024 15:27:22 -0700 Subject: [PATCH 7/7] update tests workflow --- .github/workflows/tests.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9c56992b..b3582e1a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -47,6 +47,17 @@ jobs: # MEMGPT_CONFIG_PATH: configs/server_config.yaml # run: docker compose up -d + # + - name: Run tools tests + env: + MEMGPT_PG_PORT: 8888 + MEMGPT_PG_USER: memgpt + MEMGPT_PG_PASSWORD: memgpt + MEMGPT_PG_DB: memgpt + MEMGPT_PG_HOST: localhost + MEMGPT_SERVER_PASS: test_server_token + run: | + poetry run pytest -s -vv tests/test_tools.py - name: Run server tests env: @@ -69,4 +80,4 @@ jobs: MEMGPT_SERVER_PASS: test_server_token PYTHONPATH: ${{ github.workspace }}:${{ env.PYTHONPATH }} run: | - poetry run pytest -s -vv -k "not test_concurrent_connections.py and not test_quickstart and not test_endpoints and not test_storage and not test_server and not test_openai_client" tests + poetry run pytest -s -vv -k "not test_tools.py and not test_concurrent_connections.py and not test_quickstart and not test_endpoints and not test_storage and not test_server and not test_openai_client" tests