Files
letta-server/memgpt/functions/functions.py
Charles Packer e5add4e430 Configurable presets to support easy extension of MemGPT's function set (#420)
* partial

* working schema builder, tested that it matches the hand-written schemas

* correct another schema diff

* refactor

* basic working test

* refactored preset creation to use yaml files

* added docstring-parser

* add code for dynamic function linking in agent loading

* pretty schema diff printer

* support pulling from ~/.memgpt/functions/*.py

* clean

* allow looking for system prompts in ~/.memgpt/system_prompts

* create ~/.memgpt/system_prompts if it doesn't exist

* pull presets from ~/.memgpt/presets in addition to examples folder

* add support for loading agent configs that have additional keys

---------

Co-authored-by: Sarah Wooders <sarahwooders@gmail.com>
2023-11-13 10:43:28 -08:00

78 lines
3.1 KiB
Python

import importlib
import inspect
import os
from memgpt.functions.schema_generator import generate_schema
from memgpt.constants import MEMGPT_DIR
def load_function_set(set_name):
"""Load the functions and generate schema for them"""
function_dict = {}
module_name = f"memgpt.functions.function_sets.{set_name}"
base_functions = importlib.import_module(module_name)
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) and attr.__module__ == base_functions.__name__:
if attr_name in function_dict:
raise ValueError(f"Found a duplicate of function name '{attr_name}'")
generated_schema = generate_schema(attr)
function_dict[attr_name] = {
"python_function": attr,
"json_schema": generated_schema,
}
if len(function_dict) == 0:
raise ValueError(f"No functions found in module {module_name}")
return function_dict
def load_all_function_sets(merge=True):
# functions/examples/*.py
scripts_dir = os.path.dirname(os.path.abspath(__file__)) # Get the directory of the current script
function_sets_dir = os.path.join(scripts_dir, "function_sets") # Path to the function_sets directory
# List all .py files in the directory (excluding __init__.py)
example_module_files = [f for f in os.listdir(function_sets_dir) if f.endswith(".py") and f != "__init__.py"]
# ~/.memgpt/functions/*.py
user_scripts_dir = os.path.join(MEMGPT_DIR, "functions")
# create if missing
if not os.path.exists(user_scripts_dir):
os.makedirs(user_scripts_dir)
user_module_files = [f for f in os.listdir(user_scripts_dir) if f.endswith(".py") and f != "__init__.py"]
# combine them both (pull from both examples and user-provided)
all_module_files = example_module_files + user_module_files
schemas_and_functions = {}
for file in all_module_files:
# Convert filename to module name
module_name = f"memgpt.functions.function_sets.{file[:-3]}" # Remove '.py' from filename
try:
# Load the function set
function_set = load_function_set(file[:-3]) # Pass the module part of the name
schemas_and_functions[module_name] = function_set
except ValueError as e:
print(f"Error loading function set '{module_name}': {e}")
if merge:
# Put all functions from all sets into the same level dict
merged_functions = {}
for set_name, function_set in schemas_and_functions.items():
for function_name, function_info in function_set.items():
if function_name in merged_functions:
raise ValueError(f"Duplicate function name '{function_name}' found in function set '{set_name}'")
merged_functions[function_name] = function_info
return merged_functions
else:
# Nested dict where the top level is organized by the function set name
return schemas_and_functions