switch to explicit module imports

This commit is contained in:
Peter
2023-04-06 20:00:28 -07:00
parent b4a0ef9bab
commit 3095591064
4 changed files with 39 additions and 38 deletions

View File

@@ -79,38 +79,30 @@ class CommandRegistry:
commands_list = [f"{idx + 1}. {str(cmd)}" for idx, cmd in enumerate(self.commands.values())] commands_list = [f"{idx + 1}. {str(cmd)}" for idx, cmd in enumerate(self.commands.values())]
return "\n".join(commands_list) return "\n".join(commands_list)
def scan_directory_for_plugins(self, directory: str) -> None: def import_commands(self, module_name: str) -> None:
""" """
Scans the specified directory for Python files containing command plugins. Imports the specified Python module containing command plugins.
For each file in the directory that ends with ".py", this method imports the associated module and registers any This method imports the associated module and registers any functions or
functions or classes that are decorated with the `AUTO_GPT_COMMAND_IDENTIFIER` attribute as `Command` objects. classes that are decorated with the `AUTO_GPT_COMMAND_IDENTIFIER` attribute
The registered `Command` objects are then added to the `commands` dictionary of the `CommandRegistry` object. as `Command` objects. The registered `Command` objects are then added to the
`commands` dictionary of the `CommandRegistry` object.
Args: Args:
directory (str): The directory to scan for command plugins. module_name (str): The name of the module to import for command plugins.
""" """
for file in os.listdir(directory): module = importlib.import_module(module_name)
if file.endswith(".py"):
file_path = os.path.join(directory, file)
module_name = file[:-3]
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for attr_name in dir(module):
attr = getattr(module, attr_name)
# Register decorated functions
if hasattr(attr, AUTO_GPT_COMMAND_IDENTIFIER) and getattr(attr, AUTO_GPT_COMMAND_IDENTIFIER):
self.register(attr.command)
# Register command classes
elif inspect.isclass(attr) and issubclass(attr, Command) and attr != Command:
cmd_instance = attr()
self.register(cmd_instance)
for attr_name in dir(module):
attr = getattr(module, attr_name)
# Register decorated functions
if hasattr(attr, AUTO_GPT_COMMAND_IDENTIFIER) and getattr(attr, AUTO_GPT_COMMAND_IDENTIFIER):
self.register(attr.command)
# Register command classes
elif inspect.isclass(attr) and issubclass(attr, Command) and attr != Command:
cmd_instance = attr()
self.register(cmd_instance)
def command(name: str, description: str, signature: str = None) -> Callable[..., Any]: def command(name: str, description: str, signature: str = None) -> Callable[..., Any]:
"""The command decorator is used to create Command objects from ordinary functions.""" """The command decorator is used to create Command objects from ordinary functions."""

View File

@@ -1,4 +1,5 @@
import shutil import shutil
import sys
from pathlib import Path from pathlib import Path
import pytest import pytest
@@ -112,21 +113,19 @@ class TestCommandRegistry:
assert f"(arg1: int, arg2: str)" in command_prompt assert f"(arg1: int, arg2: str)" in command_prompt
def test_scan_directory_for_mock_commands(self): def test_import_mock_commands_module(self):
"""Test that the registry can scan a directory for mocks command plugins.""" """Test that the registry can import a module with mock command plugins."""
registry = CommandRegistry() registry = CommandRegistry()
mock_commands_dir = Path("/app/auto_gpt/tests/mocks") mock_commands_module = "auto_gpt.tests.mocks.mock_commands"
import os
print(os.getcwd()) registry.import_commands(mock_commands_module)
registry.scan_directory_for_plugins(mock_commands_dir)
assert "function_based" in registry.commands assert "function_based" in registry.commands
assert registry.commands["function_based"].name == "function_based" assert registry.commands["function_based"].name == "function_based"
assert registry.commands["function_based"].description == "Function-based test command" assert registry.commands["function_based"].description == "Function-based test command"
def test_scan_directory_for_temp_command_file(self, tmp_path): def test_import_temp_command_file_module(self, tmp_path):
"""Test that the registry can scan a directory for command plugins in a temp file.""" """Test that the registry can import a command plugins module from a temp file."""
registry = CommandRegistry() registry = CommandRegistry()
# Create a temp command file # Create a temp command file
@@ -134,8 +133,14 @@ class TestCommandRegistry:
temp_commands_file = tmp_path / "mock_commands.py" temp_commands_file = tmp_path / "mock_commands.py"
shutil.copyfile(src, temp_commands_file) shutil.copyfile(src, temp_commands_file)
registry.scan_directory_for_plugins(tmp_path) # Add the temp directory to sys.path to make the module importable
print(registry.commands) sys.path.append(str(tmp_path))
temp_commands_module = "mock_commands"
registry.import_commands(temp_commands_module)
# Remove the temp directory from sys.path
sys.path.remove(str(tmp_path))
assert "function_based" in registry.commands assert "function_based" in registry.commands
assert registry.commands["function_based"].name == "function_based" assert registry.commands["function_based"].name == "function_based"

0
scripts/__init__.py Normal file
View File

View File

@@ -290,7 +290,11 @@ print('Using memory of type: ' + memory.__class__.__name__)
# Create a CommandRegistry instance and scan default folder # Create a CommandRegistry instance and scan default folder
command_registry = CommandRegistry() command_registry = CommandRegistry()
command_registry.scan_directory_for_plugins('./scripts') command_registry.import_commands('scripts.ai_functions')
command_registry.import_commands('scripts.commands')
command_registry.import_commands('scripts.execute_code')
command_registry.import_commands('scripts.agent_manager')
command_registry.import_commands('scripts.file_operations')
# Interaction Loop # Interaction Loop
while True: while True: