From 669e66a1e7628fddc2ed34215f027d4a2c6a1755 Mon Sep 17 00:00:00 2001 From: James Collins Date: Fri, 21 Jul 2023 12:01:32 -0700 Subject: [PATCH] Move all application code to an application subpackage (#5026) * Move all application code to an application subpackage * Remove main.py --- autogpt/__main__.py | 4 +- autogpt/app/__init__.py | 0 autogpt/{ => app}/cli.py | 2 +- autogpt/{ => app}/configurator.py | 30 +++++- autogpt/{ => app}/main.py | 160 ++++++++++++++++++++++++++++-- autogpt/{ => app}/setup.py | 0 autogpt/commands/__init__.py | 7 ++ autogpt/llm/utils/__init__.py | 22 ---- autogpt/logs/__init__.py | 1 - autogpt/logs/utils.py | 65 ------------ autogpt/prompts/prompt.py | 97 ------------------ benchmarks.py | 3 +- main.py | 1 - tests.py | 21 ---- tests/integration/test_setup.py | 2 +- tests/unit/test_config.py | 2 +- 16 files changed, 188 insertions(+), 229 deletions(-) create mode 100644 autogpt/app/__init__.py rename autogpt/{ => app}/cli.py (98%) rename autogpt/{ => app}/configurator.py (88%) rename autogpt/{ => app}/main.py (74%) rename autogpt/{ => app}/setup.py (100%) delete mode 100644 autogpt/logs/utils.py delete mode 100644 main.py delete mode 100644 tests.py diff --git a/autogpt/__main__.py b/autogpt/__main__.py index 128f9eea..8c11b43d 100644 --- a/autogpt/__main__.py +++ b/autogpt/__main__.py @@ -1,5 +1,5 @@ """Auto-GPT: A GPT powered AI Assistant""" -import autogpt.cli +import autogpt.app.cli if __name__ == "__main__": - autogpt.cli.main() + autogpt.app.cli.main() diff --git a/autogpt/app/__init__.py b/autogpt/app/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/autogpt/cli.py b/autogpt/app/cli.py similarity index 98% rename from autogpt/cli.py rename to autogpt/app/cli.py index 6deb00bf..dcb70c08 100644 --- a/autogpt/cli.py +++ b/autogpt/app/cli.py @@ -112,7 +112,7 @@ def main( Start an Auto-GPT assistant. """ # Put imports inside function to avoid importing everything when starting the CLI - from autogpt.main import run_auto_gpt + from autogpt.app.main import run_auto_gpt if ctx.invoked_subcommand is None: run_auto_gpt( diff --git a/autogpt/configurator.py b/autogpt/app/configurator.py similarity index 88% rename from autogpt/configurator.py rename to autogpt/app/configurator.py index fa6b4c58..29493b73 100644 --- a/autogpt/configurator.py +++ b/autogpt/app/configurator.py @@ -1,20 +1,18 @@ """Configurator module.""" from __future__ import annotations -from typing import TYPE_CHECKING +from typing import Literal import click from colorama import Back, Fore, Style from autogpt import utils +from autogpt.config import Config from autogpt.config.config import GPT_3_MODEL, GPT_4_MODEL -from autogpt.llm.utils import check_model +from autogpt.llm.api_manager import ApiManager from autogpt.logs import logger from autogpt.memory.vector import get_supported_memory_backends -if TYPE_CHECKING: - from autogpt.config import Config - def create_config( config: Config, @@ -165,3 +163,25 @@ def create_config( if skip_news: config.skip_news = True + + +def check_model( + model_name: str, + model_type: Literal["smart_llm", "fast_llm"], + config: Config, +) -> str: + """Check if model is available for use. If not, return gpt-3.5-turbo.""" + openai_credentials = config.get_openai_credentials(model_name) + api_manager = ApiManager() + models = api_manager.get_models(**openai_credentials) + + if any(model_name in m["id"] for m in models): + return model_name + + logger.typewriter_log( + "WARNING: ", + Fore.YELLOW, + f"You do not have access to {model_name}. Setting {model_type} to " + f"gpt-3.5-turbo.", + ) + return "gpt-3.5-turbo" diff --git a/autogpt/main.py b/autogpt/app/main.py similarity index 74% rename from autogpt/main.py rename to autogpt/app/main.py index ced13511..a3c7d1d8 100644 --- a/autogpt/main.py +++ b/autogpt/app/main.py @@ -11,13 +11,16 @@ from typing import Optional from colorama import Fore, Style from autogpt.agents import Agent, AgentThoughts, CommandArgs, CommandName +from autogpt.app.configurator import create_config +from autogpt.app.setup import prompt_user +from autogpt.commands import COMMAND_CATEGORIES from autogpt.config import AIConfig, Config, ConfigBuilder, check_openai_api_key -from autogpt.configurator import create_config -from autogpt.logs import logger, print_assistant_thoughts, remove_ansi_escape +from autogpt.llm.api_manager import ApiManager +from autogpt.logs import logger from autogpt.memory.vector import get_memory from autogpt.models.command_registry import CommandRegistry from autogpt.plugins import scan_plugins -from autogpt.prompts.prompt import DEFAULT_TRIGGERING_PROMPT, construct_main_ai_config +from autogpt.prompts.prompt import DEFAULT_TRIGGERING_PROMPT from autogpt.speech import say_text from autogpt.spinner import Spinner from autogpt.utils import ( @@ -30,14 +33,6 @@ from autogpt.utils import ( from autogpt.workspace import Workspace from scripts.install_plugin_deps import install_plugin_dependencies -COMMAND_CATEGORIES = [ - "autogpt.commands.execute_code", - "autogpt.commands.file_operations", - "autogpt.commands.web_search", - "autogpt.commands.web_selenium", - "autogpt.commands.task_statuses", -] - def run_auto_gpt( continuous: bool, @@ -458,3 +453,146 @@ def get_user_feedback( user_input = console_input return user_feedback, user_input, new_cycles_remaining + + +def construct_main_ai_config( + config: Config, + name: Optional[str] = None, + role: Optional[str] = None, + goals: tuple[str] = tuple(), +) -> AIConfig: + """Construct the prompt for the AI to respond to + + Returns: + str: The prompt string + """ + ai_config = AIConfig.load(config.workdir / config.ai_settings_file) + + # Apply overrides + if name: + ai_config.ai_name = name + if role: + ai_config.ai_role = role + if goals: + ai_config.ai_goals = list(goals) + + if ( + all([name, role, goals]) + or config.skip_reprompt + and all([ai_config.ai_name, ai_config.ai_role, ai_config.ai_goals]) + ): + logger.typewriter_log("Name :", Fore.GREEN, ai_config.ai_name) + logger.typewriter_log("Role :", Fore.GREEN, ai_config.ai_role) + logger.typewriter_log("Goals:", Fore.GREEN, f"{ai_config.ai_goals}") + logger.typewriter_log( + "API Budget:", + Fore.GREEN, + "infinite" if ai_config.api_budget <= 0 else f"${ai_config.api_budget}", + ) + elif all([ai_config.ai_name, ai_config.ai_role, ai_config.ai_goals]): + logger.typewriter_log( + "Welcome back! ", + Fore.GREEN, + f"Would you like me to return to being {ai_config.ai_name}?", + speak_text=True, + ) + should_continue = clean_input( + config, + f"""Continue with the last settings? +Name: {ai_config.ai_name} +Role: {ai_config.ai_role} +Goals: {ai_config.ai_goals} +API Budget: {"infinite" if ai_config.api_budget <= 0 else f"${ai_config.api_budget}"} +Continue ({config.authorise_key}/{config.exit_key}): """, + ) + if should_continue.lower() == config.exit_key: + ai_config = AIConfig() + + if any([not ai_config.ai_name, not ai_config.ai_role, not ai_config.ai_goals]): + ai_config = prompt_user(config) + ai_config.save(config.ai_settings_file) + + if config.restrict_to_workspace: + logger.typewriter_log( + "NOTE:All files/directories created by this agent can be found inside its workspace at:", + Fore.YELLOW, + f"{config.workspace_path}", + ) + # set the total api budget + api_manager = ApiManager() + api_manager.set_total_budget(ai_config.api_budget) + + # Agent Created, print message + logger.typewriter_log( + ai_config.ai_name, + Fore.LIGHTBLUE_EX, + "has been created with the following details:", + speak_text=True, + ) + + # Print the ai_config details + # Name + logger.typewriter_log("Name:", Fore.GREEN, ai_config.ai_name, speak_text=False) + # Role + logger.typewriter_log("Role:", Fore.GREEN, ai_config.ai_role, speak_text=False) + # Goals + logger.typewriter_log("Goals:", Fore.GREEN, "", speak_text=False) + for goal in ai_config.ai_goals: + logger.typewriter_log("-", Fore.GREEN, goal, speak_text=False) + + return ai_config + + +def print_assistant_thoughts( + ai_name: str, + assistant_reply_json_valid: dict, + config: Config, +) -> None: + from autogpt.speech import say_text + + assistant_thoughts_reasoning = None + assistant_thoughts_plan = None + assistant_thoughts_speak = None + assistant_thoughts_criticism = None + + assistant_thoughts = assistant_reply_json_valid.get("thoughts", {}) + assistant_thoughts_text = remove_ansi_escape(assistant_thoughts.get("text", "")) + if assistant_thoughts: + assistant_thoughts_reasoning = remove_ansi_escape( + assistant_thoughts.get("reasoning", "") + ) + assistant_thoughts_plan = remove_ansi_escape(assistant_thoughts.get("plan", "")) + assistant_thoughts_criticism = remove_ansi_escape( + assistant_thoughts.get("criticism", "") + ) + assistant_thoughts_speak = remove_ansi_escape( + assistant_thoughts.get("speak", "") + ) + logger.typewriter_log( + f"{ai_name.upper()} THOUGHTS:", Fore.YELLOW, assistant_thoughts_text + ) + logger.typewriter_log("REASONING:", Fore.YELLOW, str(assistant_thoughts_reasoning)) + if assistant_thoughts_plan: + logger.typewriter_log("PLAN:", Fore.YELLOW, "") + # If it's a list, join it into a string + if isinstance(assistant_thoughts_plan, list): + assistant_thoughts_plan = "\n".join(assistant_thoughts_plan) + elif isinstance(assistant_thoughts_plan, dict): + assistant_thoughts_plan = str(assistant_thoughts_plan) + + # Split the input_string using the newline character and dashes + lines = assistant_thoughts_plan.split("\n") + for line in lines: + line = line.lstrip("- ") + logger.typewriter_log("- ", Fore.GREEN, line.strip()) + logger.typewriter_log("CRITICISM:", Fore.YELLOW, f"{assistant_thoughts_criticism}") + # Speak the assistant's thoughts + if assistant_thoughts_speak: + if config.speak_mode: + say_text(assistant_thoughts_speak, config) + else: + logger.typewriter_log("SPEAK:", Fore.YELLOW, f"{assistant_thoughts_speak}") + + +def remove_ansi_escape(s: str) -> str: + return s.replace("\x1B", "") diff --git a/autogpt/setup.py b/autogpt/app/setup.py similarity index 100% rename from autogpt/setup.py rename to autogpt/app/setup.py diff --git a/autogpt/commands/__init__.py b/autogpt/commands/__init__.py index e69de29b..9a932b17 100644 --- a/autogpt/commands/__init__.py +++ b/autogpt/commands/__init__.py @@ -0,0 +1,7 @@ +COMMAND_CATEGORIES = [ + "autogpt.commands.execute_code", + "autogpt.commands.file_operations", + "autogpt.commands.web_search", + "autogpt.commands.web_selenium", + "autogpt.commands.task_statuses", +] diff --git a/autogpt/llm/utils/__init__.py b/autogpt/llm/utils/__init__.py index ff485260..e433476e 100644 --- a/autogpt/llm/utils/__init__.py +++ b/autogpt/llm/utils/__init__.py @@ -183,25 +183,3 @@ def create_chat_completion( if function_call else None, ) - - -def check_model( - model_name: str, - model_type: Literal["smart_llm", "fast_llm"], - config: Config, -) -> str: - """Check if model is available for use. If not, return gpt-3.5-turbo.""" - openai_credentials = config.get_openai_credentials(model_name) - api_manager = ApiManager() - models = api_manager.get_models(**openai_credentials) - - if any(model_name in m["id"] for m in models): - return model_name - - logger.typewriter_log( - "WARNING: ", - Fore.YELLOW, - f"You do not have access to {model_name}. Setting {model_type} to " - f"gpt-3.5-turbo.", - ) - return "gpt-3.5-turbo" diff --git a/autogpt/logs/__init__.py b/autogpt/logs/__init__.py index 40df21cb..b5ecc903 100644 --- a/autogpt/logs/__init__.py +++ b/autogpt/logs/__init__.py @@ -12,4 +12,3 @@ from .log_cycle import ( LogCycleHandler, ) from .logger import Logger, logger -from .utils import print_assistant_thoughts, remove_ansi_escape diff --git a/autogpt/logs/utils.py b/autogpt/logs/utils.py deleted file mode 100644 index 637c917f..00000000 --- a/autogpt/logs/utils.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -from colorama import Fore - -if TYPE_CHECKING: - from autogpt.config import Config - -from .logger import logger - - -def print_assistant_thoughts( - ai_name: str, - assistant_reply_json_valid: dict, - config: Config, -) -> None: - from autogpt.speech import say_text - - assistant_thoughts_reasoning = None - assistant_thoughts_plan = None - assistant_thoughts_speak = None - assistant_thoughts_criticism = None - - assistant_thoughts = assistant_reply_json_valid.get("thoughts", {}) - assistant_thoughts_text = remove_ansi_escape(assistant_thoughts.get("text", "")) - if assistant_thoughts: - assistant_thoughts_reasoning = remove_ansi_escape( - assistant_thoughts.get("reasoning", "") - ) - assistant_thoughts_plan = remove_ansi_escape(assistant_thoughts.get("plan", "")) - assistant_thoughts_criticism = remove_ansi_escape( - assistant_thoughts.get("criticism", "") - ) - assistant_thoughts_speak = remove_ansi_escape( - assistant_thoughts.get("speak", "") - ) - logger.typewriter_log( - f"{ai_name.upper()} THOUGHTS:", Fore.YELLOW, assistant_thoughts_text - ) - logger.typewriter_log("REASONING:", Fore.YELLOW, str(assistant_thoughts_reasoning)) - if assistant_thoughts_plan: - logger.typewriter_log("PLAN:", Fore.YELLOW, "") - # If it's a list, join it into a string - if isinstance(assistant_thoughts_plan, list): - assistant_thoughts_plan = "\n".join(assistant_thoughts_plan) - elif isinstance(assistant_thoughts_plan, dict): - assistant_thoughts_plan = str(assistant_thoughts_plan) - - # Split the input_string using the newline character and dashes - lines = assistant_thoughts_plan.split("\n") - for line in lines: - line = line.lstrip("- ") - logger.typewriter_log("- ", Fore.GREEN, line.strip()) - logger.typewriter_log("CRITICISM:", Fore.YELLOW, f"{assistant_thoughts_criticism}") - # Speak the assistant's thoughts - if assistant_thoughts_speak: - if config.speak_mode: - say_text(assistant_thoughts_speak, config) - else: - logger.typewriter_log("SPEAK:", Fore.YELLOW, f"{assistant_thoughts_speak}") - - -def remove_ansi_escape(s: str) -> str: - return s.replace("\x1B", "") diff --git a/autogpt/prompts/prompt.py b/autogpt/prompts/prompt.py index d275abc2..b64f11f5 100644 --- a/autogpt/prompts/prompt.py +++ b/autogpt/prompts/prompt.py @@ -1,15 +1,6 @@ -from typing import Optional - -from colorama import Fore - -from autogpt.config.ai_config import AIConfig from autogpt.config.config import Config from autogpt.config.prompt_config import PromptConfig -from autogpt.llm.api_manager import ApiManager -from autogpt.logs import logger from autogpt.prompts.generator import PromptGenerator -from autogpt.setup import prompt_user -from autogpt.utils import clean_input DEFAULT_TRIGGERING_PROMPT = "Determine exactly one command to use, and respond using the JSON schema specified previously:" @@ -42,91 +33,3 @@ def build_default_prompt_generator(config: Config) -> PromptGenerator: prompt_generator.add_performance_evaluation(performance_evaluation) return prompt_generator - - -def construct_main_ai_config( - config: Config, - name: Optional[str] = None, - role: Optional[str] = None, - goals: tuple[str] = tuple(), -) -> AIConfig: - """Construct the prompt for the AI to respond to - - Returns: - str: The prompt string - """ - ai_config = AIConfig.load(config.workdir / config.ai_settings_file) - - # Apply overrides - if name: - ai_config.ai_name = name - if role: - ai_config.ai_role = role - if goals: - ai_config.ai_goals = list(goals) - - if ( - all([name, role, goals]) - or config.skip_reprompt - and all([ai_config.ai_name, ai_config.ai_role, ai_config.ai_goals]) - ): - logger.typewriter_log("Name :", Fore.GREEN, ai_config.ai_name) - logger.typewriter_log("Role :", Fore.GREEN, ai_config.ai_role) - logger.typewriter_log("Goals:", Fore.GREEN, f"{ai_config.ai_goals}") - logger.typewriter_log( - "API Budget:", - Fore.GREEN, - "infinite" if ai_config.api_budget <= 0 else f"${ai_config.api_budget}", - ) - elif all([ai_config.ai_name, ai_config.ai_role, ai_config.ai_goals]): - logger.typewriter_log( - "Welcome back! ", - Fore.GREEN, - f"Would you like me to return to being {ai_config.ai_name}?", - speak_text=True, - ) - should_continue = clean_input( - config, - f"""Continue with the last settings? -Name: {ai_config.ai_name} -Role: {ai_config.ai_role} -Goals: {ai_config.ai_goals} -API Budget: {"infinite" if ai_config.api_budget <= 0 else f"${ai_config.api_budget}"} -Continue ({config.authorise_key}/{config.exit_key}): """, - ) - if should_continue.lower() == config.exit_key: - ai_config = AIConfig() - - if any([not ai_config.ai_name, not ai_config.ai_role, not ai_config.ai_goals]): - ai_config = prompt_user(config) - ai_config.save(config.workdir / config.ai_settings_file) - - if config.restrict_to_workspace: - logger.typewriter_log( - "NOTE:All files/directories created by this agent can be found inside its workspace at:", - Fore.YELLOW, - f"{config.workspace_path}", - ) - # set the total api budget - api_manager = ApiManager() - api_manager.set_total_budget(ai_config.api_budget) - - # Agent Created, print message - logger.typewriter_log( - ai_config.ai_name, - Fore.LIGHTBLUE_EX, - "has been created with the following details:", - speak_text=True, - ) - - # Print the ai_config details - # Name - logger.typewriter_log("Name:", Fore.GREEN, ai_config.ai_name, speak_text=False) - # Role - logger.typewriter_log("Role:", Fore.GREEN, ai_config.ai_role, speak_text=False) - # Goals - logger.typewriter_log("Goals:", Fore.GREEN, "", speak_text=False) - for goal in ai_config.ai_goals: - logger.typewriter_log("-", Fore.GREEN, goal, speak_text=False) - - return ai_config diff --git a/benchmarks.py b/benchmarks.py index 2b4e5fec..589b3f75 100644 --- a/benchmarks.py +++ b/benchmarks.py @@ -1,8 +1,9 @@ from pathlib import Path from autogpt.agents import Agent +from autogpt.app.main import run_interaction_loop +from autogpt.commands import COMMAND_CATEGORIES from autogpt.config import AIConfig, Config, ConfigBuilder -from autogpt.main import COMMAND_CATEGORIES, run_interaction_loop from autogpt.memory.vector import get_memory from autogpt.models.command_registry import CommandRegistry from autogpt.prompts.prompt import DEFAULT_TRIGGERING_PROMPT diff --git a/main.py b/main.py deleted file mode 100644 index 160addc3..00000000 --- a/main.py +++ /dev/null @@ -1 +0,0 @@ -from autogpt import main diff --git a/tests.py b/tests.py deleted file mode 100644 index 62f76da8..00000000 --- a/tests.py +++ /dev/null @@ -1,21 +0,0 @@ -import unittest - -import coverage - -if __name__ == "__main__": - # Start coverage collection - cov = coverage.Coverage() - cov.start() - - # Load all tests from the 'autogpt/tests' package - suite = unittest.defaultTestLoader.discover("./tests") - - # Run the tests - unittest.TextTestRunner().run(suite) - - # Stop coverage collection - cov.stop() - cov.save() - - # Report the coverage - cov.report(show_missing=True) diff --git a/tests/integration/test_setup.py b/tests/integration/test_setup.py index b74eebaf..f4bb9a5c 100644 --- a/tests/integration/test_setup.py +++ b/tests/integration/test_setup.py @@ -2,8 +2,8 @@ from unittest.mock import patch import pytest +from autogpt.app.setup import generate_aiconfig_automatic, prompt_user from autogpt.config.ai_config import AIConfig -from autogpt.setup import generate_aiconfig_automatic, prompt_user @pytest.mark.vcr diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 066ca03d..6445ae78 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -8,8 +8,8 @@ from unittest.mock import patch import pytest +from autogpt.app.configurator import GPT_3_MODEL, GPT_4_MODEL, create_config from autogpt.config import Config, ConfigBuilder -from autogpt.configurator import GPT_3_MODEL, GPT_4_MODEL, create_config from autogpt.workspace.workspace import Workspace