Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/move functions in app to agent #4957

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 92 additions & 3 deletions autogpt/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from autogpt.config import Config
from autogpt.config.ai_config import AIConfig
from autogpt.json_utils.utilities import extract_json_from_response, validate_json
from autogpt.llm import ChatModelResponse
from autogpt.llm.chat import chat_with_ai
from autogpt.llm.providers.openai import OPEN_AI_CHAT_MODELS
from autogpt.llm.utils import count_string_tokens
Expand Down Expand Up @@ -86,9 +87,6 @@
self.smart_token_limit = OPEN_AI_CHAT_MODELS.get(config.smart_llm).max_tokens

def start_interaction_loop(self):
# Avoid circular imports
from autogpt.app import execute_command, extract_command

# Interaction Loop
self.cycle_count = 0
command_name = None
Expand Down Expand Up @@ -307,3 +305,94 @@
logger.typewriter_log(
"SYSTEM: ", Fore.YELLOW, "Unable to execute command"
)


def extract_command(
assistant_reply_json: dict, assistant_reply: ChatModelResponse, config: Config
):
"""Parse the response and return the command name and arguments

Args:
assistant_reply_json (dict): The response object from the AI
assistant_reply (ChatModelResponse): The model response from the AI
config (Config): The config object

Returns:
tuple: The command name and arguments

Raises:
json.decoder.JSONDecodeError: If the response is not valid JSON

Exception: If any other error occurs
"""
if config.openai_functions:
if assistant_reply.function_call is None:
return "Error:", "No 'function_call' in assistant reply"
assistant_reply_json["command"] = {

Check warning on line 331 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L330-L331

Added lines #L330 - L331 were not covered by tests
"name": assistant_reply.function_call.name,
"args": json.loads(assistant_reply.function_call.arguments),
}
try:
if "command" not in assistant_reply_json:
return "Error:", "Missing 'command' object in JSON"

Check warning on line 337 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L337

Added line #L337 was not covered by tests

if not isinstance(assistant_reply_json, dict):
return (

Check warning on line 340 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L340

Added line #L340 was not covered by tests
"Error:",
f"The previous message sent was not a dictionary {assistant_reply_json}",
)

command = assistant_reply_json["command"]
if not isinstance(command, dict):
return "Error:", "'command' object is not a dictionary"

Check warning on line 347 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L347

Added line #L347 was not covered by tests

if "name" not in command:
return "Error:", "Missing 'name' field in 'command' object"

Check warning on line 350 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L350

Added line #L350 was not covered by tests

command_name = command["name"]

# Use an empty dictionary if 'args' field is not present in 'command' object
arguments = command.get("args", {})

return command_name, arguments
except json.decoder.JSONDecodeError:
return "Error:", "Invalid JSON"

Check warning on line 359 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L359

Added line #L359 was not covered by tests
# All other errors, return "Error: + error message"
except Exception as e:
return "Error:", str(e)

Check warning on line 362 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L361-L362

Added lines #L361 - L362 were not covered by tests


def execute_command(
command_name: str,
arguments: dict[str, str],
agent: Agent,
):
"""Execute the command and return the result

Args:
command_name (str): The name of the command to execute
arguments (dict): The arguments for the command
agent (Agent): The agent that is executing the command

Returns:
str: The result of the command
"""
try:
# Execute a native command with the same name or alias, if it exists
if command := agent.command_registry.get_command(command_name):
return command(**arguments, agent=agent)

# Handle non-native commands (e.g. from plugins)
for command in agent.ai_config.prompt_generator.commands:
if (
command_name == command["label"].lower()
or command_name == command["name"].lower()
):
return command["function"](**arguments)

raise RuntimeError(

Check warning on line 393 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L393

Added line #L393 was not covered by tests
f"Cannot execute '{command_name}': unknown command."
" Do not try to use this command again."
)
except Exception as e:
return f"Error: {str(e)}"

Check warning on line 398 in autogpt/agent/agent.py

View check run for this annotation

Codecov / codecov/patch

autogpt/agent/agent.py#L398

Added line #L398 was not covered by tests
114 changes: 0 additions & 114 deletions autogpt/app.py

This file was deleted.

1 change: 0 additions & 1 deletion autogpt/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"autogpt.commands.file_operations",
"autogpt.commands.web_search",
"autogpt.commands.web_selenium",
"autogpt.app",
"autogpt.commands.task_statuses",
]

Expand Down
57 changes: 19 additions & 38 deletions tests/unit/test_agent.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,27 @@
from unittest.mock import MagicMock

import pytest

from autogpt.agent import Agent
from autogpt.config import AIConfig
from autogpt.config.config import Config


@pytest.fixture
def agent(config: Config):
ai_name = "Test AI"
memory = MagicMock()
next_action_count = 0
command_registry = MagicMock()
ai_config = AIConfig(ai_name=ai_name)
system_prompt = "System prompt"
triggering_prompt = "Triggering prompt"
workspace_directory = "workspace_directory"

agent = Agent(
ai_name=ai_name,
memory=memory,
next_action_count=next_action_count,
command_registry=command_registry,
ai_config=ai_config,
config=config,
system_prompt=system_prompt,
triggering_prompt=triggering_prompt,
workspace_directory=workspace_directory,
)
return agent
from autogpt.agent.agent import Agent, execute_command


def test_agent_initialization(agent: Agent):
assert agent.ai_name == "Test AI"
assert agent.memory == agent.memory
assert agent.ai_name == "Base"
assert agent.history.messages == []
assert agent.next_action_count == 0
assert agent.command_registry == agent.command_registry
assert agent.ai_config == agent.ai_config
assert agent.system_prompt == "System prompt"
assert agent.triggering_prompt == "Triggering prompt"


def test_execute_command_plugin(agent: Agent):
"""Test that executing a command that came from a plugin works as expected"""
command_name = "check_plan"
agent.ai_config.prompt_generator.add_command(
command_name,
"Read the plan.md with the next goals to achieve",
{},
lambda: "hi",
)
command_result = execute_command(
command_name=command_name,
arguments={},
agent=agent,
)
assert command_result == "hi"


# More test methods can be added for specific agent interactions
Expand Down
23 changes: 0 additions & 23 deletions tests/unit/test_execute_command.py

This file was deleted.