diff --git a/scripts/auto_gpt/__init__.py b/auto_gpt/__init__.py similarity index 100% rename from scripts/auto_gpt/__init__.py rename to auto_gpt/__init__.py diff --git a/scripts/auto_gpt/commands.py b/auto_gpt/commands.py similarity index 78% rename from scripts/auto_gpt/commands.py rename to auto_gpt/commands.py index fb05fdb8f2..0133c66531 100644 --- a/scripts/auto_gpt/commands.py +++ b/auto_gpt/commands.py @@ -13,7 +13,6 @@ class Command: Attributes: name (str): The name of the command. description (str): A brief description of what the command does. - method (Callable[..., Any]): The function that the command executes. signature (str): The signature of the function that the command executes. Defaults to None. """ @@ -46,9 +45,15 @@ class CommandRegistry: def _reload_module(self, module: Any) -> Any: return importlib.reload(module) - def register_command(self, cmd: Command) -> None: + def register(self, cmd: Command) -> None: self.commands[cmd.name] = cmd + def unregister(self, command_name: str): + if command_name in self.commands: + del self.commands[command_name] + else: + raise KeyError(f"Command '{command_name}' not found in registry.") + def reload_commands(self) -> None: """Reloads all loaded command plugins.""" for cmd_name in self.commands: @@ -59,10 +64,13 @@ class CommandRegistry: reloaded_module.register(self) def get_command(self, name: str) -> Callable[..., Any]: - return self.commands.get(name) + return self.commands[name] - def list_commands(self) -> List[str]: - return [str(cmd) for cmd in self.commands.values()] + def call(self, command_name: str, **kwargs) -> Any: + if command_name not in self.commands: + raise KeyError(f"Command '{command_name}' not found in registry.") + command = self.commands[command_name] + return command(**kwargs) def command_prompt(self) -> str: """ @@ -85,17 +93,23 @@ class CommandRegistry: for file in os.listdir(directory): if file.endswith(".py"): + file_path = os.path.join(directory, file) module_name = file[:-3] - module = importlib.import_module(module_name) + + 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_command(attr.register_command) + self.register(attr.command) # Register command classes elif inspect.isclass(attr) and issubclass(attr, Command) and attr != Command: cmd_instance = attr() - self.register_command(cmd_instance) + self.register(cmd_instance) def command(name: str, description: str, signature: str = None) -> Callable[..., Any]: @@ -106,7 +120,8 @@ def command(name: str, description: str, signature: str = None) -> Callable[..., def wrapper(*args, **kwargs) -> Any: return func(*args, **kwargs) - wrapper.register_command = cmd + wrapper.command = cmd + setattr(wrapper, AUTO_GPT_COMMAND_IDENTIFIER, True) return wrapper diff --git a/scripts/auto_gpt/tests/__init__.py b/auto_gpt/tests/__init__.py similarity index 100% rename from scripts/auto_gpt/tests/__init__.py rename to auto_gpt/tests/__init__.py diff --git a/scripts/auto_gpt/tests/mock/__init__.py b/auto_gpt/tests/mocks/__init__.py similarity index 100% rename from scripts/auto_gpt/tests/mock/__init__.py rename to auto_gpt/tests/mocks/__init__.py diff --git a/auto_gpt/tests/mocks/mock_commands.py b/auto_gpt/tests/mocks/mock_commands.py new file mode 100644 index 0000000000..d68ceb8133 --- /dev/null +++ b/auto_gpt/tests/mocks/mock_commands.py @@ -0,0 +1,6 @@ +from auto_gpt.commands import Command, command + + +@command('function_based', 'Function-based test command') +def function_based(arg1: int, arg2: str) -> str: + return f'{arg1} - {arg2}' diff --git a/scripts/auto_gpt/tests/test_commands.py b/auto_gpt/tests/test_commands.py similarity index 53% rename from scripts/auto_gpt/tests/test_commands.py rename to auto_gpt/tests/test_commands.py index e2681798a5..fc0ccb3d40 100644 --- a/scripts/auto_gpt/tests/test_commands.py +++ b/auto_gpt/tests/test_commands.py @@ -1,7 +1,8 @@ +import shutil from pathlib import Path import pytest -from commands import Command, CommandRegistry +from auto_gpt.commands import Command, CommandRegistry class TestCommand: @@ -15,7 +16,7 @@ class TestCommand: assert cmd.name == "example" assert cmd.description == "Example command" assert cmd.method == self.example_function - assert cmd.signature == "arg1: int, arg2: str" + assert cmd.signature == "(arg1: int, arg2: str) -> str" def test_command_call(self): cmd = Command(name="example", description="Example command", method=self.example_function) @@ -27,12 +28,12 @@ class TestCommand: cmd = Command(name="example", description="Example command", method=self.example_function) with pytest.raises(TypeError): - cmd(arg1="invalid", arg2="test") + cmd(arg1="invalid", does_not_exist="test") def test_command_default_signature(self): cmd = Command(name="example", description="Example command", method=self.example_function) - assert cmd.signature == "arg1: int, arg2: str" + assert cmd.signature == "(arg1: int, arg2: str) -> str" def test_command_custom_signature(self): custom_signature = "custom_arg1: int, custom_arg2: str" @@ -54,8 +55,8 @@ class TestCommandRegistry: registry.register(cmd) - assert cmd.name in registry._commands - assert registry._commands[cmd.name] == cmd + assert cmd.name in registry.commands + assert registry.commands[cmd.name] == cmd def test_unregister_command(self): """Test that a command can be unregistered from the registry.""" @@ -65,7 +66,7 @@ class TestCommandRegistry: registry.register(cmd) registry.unregister(cmd.name) - assert cmd.name not in registry._commands + assert cmd.name not in registry.commands def test_get_command(self): """Test that a command can be retrieved from the registry.""" @@ -73,7 +74,7 @@ class TestCommandRegistry: cmd = Command(name="example", description="Example command", method=self.example_function) registry.register(cmd) - retrieved_cmd = registry.get(cmd.name) + retrieved_cmd = registry.get_command(cmd.name) assert retrieved_cmd == cmd @@ -82,7 +83,7 @@ class TestCommandRegistry: registry = CommandRegistry() with pytest.raises(KeyError): - registry.get("nonexistent_command") + registry.get_command("nonexistent_command") def test_call_command(self): """Test that a command can be called through the registry.""" @@ -101,74 +102,41 @@ class TestCommandRegistry: with pytest.raises(KeyError): registry.call("nonexistent_command", arg1=1, arg2="test") - def test_get_command_list(self): - """Test that a list of registered commands can be retrieved.""" - registry = CommandRegistry() - cmd1 = Command(name="example1", description="Example command 1", method=self.example_function) - cmd2 = Command(name="example2", description="Example command 2", method=self.example_function) - - registry.register(cmd1) - registry.register(cmd2) - command_list = registry.get_command_list() - - assert len(command_list) == 2 - assert cmd1.name in command_list - assert cmd2.name in command_list - def test_get_command_prompt(self): """Test that the command prompt is correctly formatted.""" registry = CommandRegistry() cmd = Command(name="example", description="Example command", method=self.example_function) registry.register(cmd) - command_prompt = registry.get_command_prompt() + command_prompt = registry.command_prompt() - assert f"{cmd.name}: {cmd.description}, args: {cmd.signature}" in command_prompt + assert f"(arg1: int, arg2: str)" in command_prompt def test_scan_directory_for_mock_commands(self): - """Test that the registry can scan a directory for mock command plugins.""" + """Test that the registry can scan a directory for mocks command plugins.""" registry = CommandRegistry() - mock_commands_dir = Path("auto_gpt/tests/mocks") + mock_commands_dir = Path("/app/auto_gpt/tests/mocks") + import os + print(os.getcwd()) registry.scan_directory_for_plugins(mock_commands_dir) - assert "mock_class_based" in registry._commands - assert registry._commands["mock_class_based"].name == "mock_class_based" - assert registry._commands["mock_class_based"].description == "Mock class-based command" - - assert "mock_function_based" in registry._commands - assert registry._commands["mock_function_based"].name == "mock_function_based" - assert registry._commands["mock_function_based"].description == "Mock function-based command" + assert "function_based" in registry.commands + assert registry.commands["function_based"].name == "function_based" + assert registry.commands["function_based"].description == "Function-based test command" def test_scan_directory_for_temp_command_file(self, tmp_path): """Test that the registry can scan a directory for command plugins in a temp file.""" registry = CommandRegistry() - temp_commands_dir = tmp_path / "temp_commands" - temp_commands_dir.mkdir() # Create a temp command file - temp_commands_file = temp_commands_dir / "temp_commands.py" - temp_commands_content = ( - "from commands import Command, command\n\n" - "class TempCommand(Command):\n" - " def __init__(self):\n" - " super().__init__(name='temp_class_based', description='Temp class-based command')\n\n" - " def __call__(self, arg1: int, arg2: str) -> str:\n" - " return f'{arg1} - {arg2}'\n\n" - "@command('temp_function_based', 'Temp function-based command')\n" - "def temp_function_based(arg1: int, arg2: str) -> str:\n" - " return f'{arg1} - {arg2}'\n" - ) + src = Path("/app/auto_gpt/tests/mocks/mock_commands.py") + temp_commands_file = tmp_path / "mock_commands.py" + shutil.copyfile(src, temp_commands_file) - with open(temp_commands_file, "w") as f: - f.write(temp_commands_content) + registry.scan_directory_for_plugins(tmp_path) + print(registry.commands) - registry.scan_directory_for_plugins(temp_commands_dir) - - assert "temp_class_based" in registry._commands - assert registry._commands["temp_class_based"].name == "temp_class_based" - assert registry._commands["temp_class_based"].description == "Temp class-based command" - - assert "temp_function_based" in registry._commands - assert registry._commands["temp_function_based"].name == "temp_function_based" - assert registry._commands["temp_function_based"].description == "Temp function-based command" + assert "function_based" in registry.commands + assert registry.commands["function_based"].name == "function_based" + assert registry.commands["function_based"].description == "Function-based test command" diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index 175dffa2fb..c7cb9a5a00 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -1,8 +1,7 @@ -from typing import List, Optional +from typing import List import json from config import Config from call_ai_function import call_ai_function -from json_parser import fix_and_parse_json from auto_gpt.commands import command cfg = Config() diff --git a/scripts/auto_gpt/tests/mock/mock_commands.py b/scripts/auto_gpt/tests/mock/mock_commands.py deleted file mode 100644 index 3514b62af1..0000000000 --- a/scripts/auto_gpt/tests/mock/mock_commands.py +++ /dev/null @@ -1,12 +0,0 @@ -from commands import Command, command - -class TestCommand(Command): - def __init__(self): - super().__init__(name='class_based', description='Class-based test command') - - def __call__(self, arg1: int, arg2: str) -> str: - return f'{arg1} - {arg2}' - -@command('function_based', 'Function-based test command') -def function_based(arg1: int, arg2: str) -> str: - return f'{arg1} - {arg2}' diff --git a/scripts/commands.py b/scripts/commands.py index 78f5dbe30d..2a78031ae1 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,6 +1,5 @@ import browse import json -from memory import PineconeMemory import datetime import agent_manager as agents import speak