diff --git a/autogpt/cli.py b/autogpt/cli.py index 3b45b50174f..690c16261ae 100644 --- a/autogpt/cli.py +++ b/autogpt/cli.py @@ -1,4 +1,6 @@ """Main script for the autogpt package.""" +from typing import Optional + import click @@ -65,6 +67,22 @@ is_flag=True, help="Installs external dependencies for 3rd party plugins.", ) +@click.option( + "--ai-name", + type=str, + help="AI name override", +) +@click.option( + "--ai-role", + type=str, + help="AI role override", +) +@click.option( + "--ai-goal", + type=str, + multiple=True, + help="AI goal override; may be used multiple times to pass multiple goals", +) @click.pass_context def main( ctx: click.Context, @@ -83,6 +101,9 @@ def main( skip_news: bool, workspace_directory: str, install_plugin_deps: bool, + ai_name: Optional[str], + ai_role: Optional[str], + ai_goal: tuple[str], ) -> None: """ Welcome to AutoGPT an experimental open-source application showcasing the capabilities of the GPT-4 pushing the boundaries of AI. @@ -109,6 +130,9 @@ def main( skip_news, workspace_directory, install_plugin_deps, + ai_name, + ai_role, + ai_goal, ) diff --git a/autogpt/config/ai_config.py b/autogpt/config/ai_config.py index 3c645abe36f..a2952c9ddce 100644 --- a/autogpt/config/ai_config.py +++ b/autogpt/config/ai_config.py @@ -35,7 +35,7 @@ def __init__( self, ai_name: str = "", ai_role: str = "", - ai_goals: list | None = None, + ai_goals: list[str] = [], api_budget: float = 0.0, ) -> None: """ @@ -49,8 +49,6 @@ def __init__( Returns: None """ - if ai_goals is None: - ai_goals = [] self.ai_name = ai_name self.ai_role = ai_role self.ai_goals = ai_goals @@ -61,13 +59,12 @@ def __init__( @staticmethod def load(ai_settings_file: str = SAVE_FILE) -> "AIConfig": """ - Returns class object with parameters (ai_name, ai_role, ai_goals, api_budget) loaded from - yaml file if yaml file exists, - else returns class with no parameters. + Returns class object with parameters (ai_name, ai_role, ai_goals, api_budget) + loaded from yaml file if yaml file exists, else returns class with no parameters. Parameters: - ai_settings_file (int): The path to the config yaml file. - DEFAULT: "../ai_settings.yaml" + ai_settings_file (int): The path to the config yaml file. + DEFAULT: "../ai_settings.yaml" Returns: cls (object): An instance of given cls object diff --git a/autogpt/config/config.py b/autogpt/config/config.py index 4224cee4f0b..fc76d084f06 100644 --- a/autogpt/config/config.py +++ b/autogpt/config/config.py @@ -4,7 +4,7 @@ import contextlib import os import re -from typing import Dict, Union +from typing import Dict, Optional, Union import yaml from colorama import Fore diff --git a/autogpt/main.py b/autogpt/main.py index 3058702983b..c1b79c78366 100644 --- a/autogpt/main.py +++ b/autogpt/main.py @@ -2,6 +2,7 @@ import logging import sys from pathlib import Path +from typing import Optional from colorama import Fore, Style @@ -48,6 +49,9 @@ def run_auto_gpt( skip_news: bool, workspace_directory: str | Path, install_plugin_deps: bool, + ai_name: Optional[str] = None, + ai_role: Optional[str] = None, + ai_goals: tuple[str] = tuple(), ): # Configure logging before we do anything else. logger.set_level(logging.DEBUG if debug else logging.INFO) @@ -154,11 +158,14 @@ def run_auto_gpt( f"reason - {command.disabled_reason or 'Disabled by current config.'}" ) - ai_name = "" - ai_config = construct_main_ai_config(config) + ai_config = construct_main_ai_config( + config, + name=ai_name, + role=ai_role, + goals=ai_goals, + ) ai_config.command_registry = command_registry - if ai_config.ai_name: - ai_name = ai_config.ai_name + ai_name = ai_config.ai_name # print(prompt) # Initialize variables next_action_count = 0 diff --git a/autogpt/prompts/prompt.py b/autogpt/prompts/prompt.py index 16d5c7e7ea1..b5a0ec882b4 100644 --- a/autogpt/prompts/prompt.py +++ b/autogpt/prompts/prompt.py @@ -1,3 +1,5 @@ +from typing import Optional + from colorama import Fore from autogpt.config.ai_config import AIConfig @@ -42,14 +44,32 @@ def build_default_prompt_generator(config: Config) -> PromptGenerator: return prompt_generator -def construct_main_ai_config(config: Config) -> AIConfig: +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.ai_settings_file) - if config.skip_reprompt and ai_config.ai_name: + + # 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}") @@ -58,7 +78,7 @@ def construct_main_ai_config(config: Config) -> AIConfig: Fore.GREEN, "infinite" if ai_config.api_budget <= 0 else f"${ai_config.api_budget}", ) - elif ai_config.ai_name: + elif all([ai_config.ai_name, ai_config.ai_role, ai_config.ai_goals]): logger.typewriter_log( "Welcome back! ", Fore.GREEN, @@ -77,7 +97,7 @@ def construct_main_ai_config(config: Config) -> AIConfig: if should_continue.lower() == config.exit_key: ai_config = AIConfig() - if not ai_config.ai_name: + 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) diff --git a/autogpt/setup.py b/autogpt/setup.py index fd8d33d8d71..fc42924323e 100644 --- a/autogpt/setup.py +++ b/autogpt/setup.py @@ -1,5 +1,6 @@ """Set up the AI and its goals""" import re +from typing import Optional from colorama import Fore, Style from jinja2 import Template @@ -17,14 +18,18 @@ ) -def prompt_user(config: Config) -> AIConfig: +def prompt_user( + config: Config, ai_config_template: Optional[AIConfig] = None +) -> AIConfig: """Prompt the user for input + Params: + config (Config): The Config object + ai_config_template (AIConfig): The AIConfig object to use as a template + Returns: AIConfig: The AIConfig object tailored to the user's input """ - ai_name = "" - ai_config = None # Construct the prompt logger.typewriter_log( @@ -34,29 +39,39 @@ def prompt_user(config: Config) -> AIConfig: speak_text=True, ) - # Get user desire - logger.typewriter_log( - "Create an AI-Assistant:", - Fore.GREEN, - "input '--manual' to enter manual mode.", - speak_text=True, + ai_config_template_provided = ai_config_template is not None and any( + [ + ai_config_template.ai_goals, + ai_config_template.ai_name, + ai_config_template.ai_role, + ] ) - user_desire = utils.clean_input( - config, f"{Fore.LIGHTBLUE_EX}I want Auto-GPT to{Style.RESET_ALL}: " - ) + user_desire = "" + if not ai_config_template_provided: + # Get user desire if command line overrides have not been passed in + logger.typewriter_log( + "Create an AI-Assistant:", + Fore.GREEN, + "input '--manual' to enter manual mode.", + speak_text=True, + ) + + user_desire = utils.clean_input( + config, f"{Fore.LIGHTBLUE_EX}I want Auto-GPT to{Style.RESET_ALL}: " + ) - if user_desire == "": + if user_desire.strip() == "": user_desire = DEFAULT_USER_DESIRE_PROMPT # Default prompt - # If user desire contains "--manual" - if "--manual" in user_desire: + # If user desire contains "--manual" or we have overridden any of the AI configuration + if "--manual" in user_desire or ai_config_template_provided: logger.typewriter_log( "Manual Mode Selected", Fore.GREEN, speak_text=True, ) - return generate_aiconfig_manual(config) + return generate_aiconfig_manual(config, ai_config_template) else: try: @@ -72,7 +87,9 @@ def prompt_user(config: Config) -> AIConfig: return generate_aiconfig_manual(config) -def generate_aiconfig_manual(config: Config) -> AIConfig: +def generate_aiconfig_manual( + config: Config, ai_config_template: Optional[AIConfig] = None +) -> AIConfig: """ Interactively create an AI configuration by prompting the user to provide the name, role, and goals of the AI. @@ -80,6 +97,10 @@ def generate_aiconfig_manual(config: Config) -> AIConfig: an AIConfig object. The user will be asked to provide a name and role for the AI, as well as up to five goals. If the user does not provide a value for any of the fields, default values will be used. + Params: + config (Config): The Config object + ai_config_template (AIConfig): The AIConfig object to use as a template + Returns: AIConfig: An AIConfig object containing the user-defined or default AI name, role, and goals. """ @@ -93,11 +114,15 @@ def generate_aiconfig_manual(config: Config) -> AIConfig: speak_text=True, ) - # Get AI Name from User - logger.typewriter_log( - "Name your AI: ", Fore.GREEN, "For example, 'Entrepreneur-GPT'" - ) - ai_name = utils.clean_input(config, "AI Name: ") + if ai_config_template and ai_config_template.ai_name: + ai_name = ai_config_template.ai_name + else: + ai_name = "" + # Get AI Name from User + logger.typewriter_log( + "Name your AI: ", Fore.GREEN, "For example, 'Entrepreneur-GPT'" + ) + ai_name = utils.clean_input(config, "AI Name: ") if ai_name == "": ai_name = "Entrepreneur-GPT" @@ -105,34 +130,40 @@ def generate_aiconfig_manual(config: Config) -> AIConfig: f"{ai_name} here!", Fore.LIGHTBLUE_EX, "I am at your service.", speak_text=True ) - # Get AI Role from User - logger.typewriter_log( - "Describe your AI's role: ", - Fore.GREEN, - "For example, 'an AI designed to autonomously develop and run businesses with" - " the sole goal of increasing your net worth.'", - ) - ai_role = utils.clean_input(config, f"{ai_name} is: ") + if ai_config_template and ai_config_template.ai_role: + ai_role = ai_config_template.ai_role + else: + # Get AI Role from User + logger.typewriter_log( + "Describe your AI's role: ", + Fore.GREEN, + "For example, 'an AI designed to autonomously develop and run businesses with" + " the sole goal of increasing your net worth.'", + ) + ai_role = utils.clean_input(config, f"{ai_name} is: ") if ai_role == "": ai_role = "an AI designed to autonomously develop and run businesses with the" " sole goal of increasing your net worth." - # Enter up to 5 goals for the AI - logger.typewriter_log( - "Enter up to 5 goals for your AI: ", - Fore.GREEN, - "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage" - " multiple businesses autonomously'", - ) - logger.info("Enter nothing to load defaults, enter nothing when finished.") - ai_goals = [] - for i in range(5): - ai_goal = utils.clean_input( - config, f"{Fore.LIGHTBLUE_EX}Goal{Style.RESET_ALL} {i+1}: " + if ai_config_template and ai_config_template.ai_goals: + ai_goals = ai_config_template.ai_goals + else: + # Enter up to 5 goals for the AI + logger.typewriter_log( + "Enter up to 5 goals for your AI: ", + Fore.GREEN, + "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage" + " multiple businesses autonomously'", ) - if ai_goal == "": - break - ai_goals.append(ai_goal) + logger.info("Enter nothing to load defaults, enter nothing when finished.") + ai_goals = [] + for i in range(5): + ai_goal = utils.clean_input( + config, f"{Fore.LIGHTBLUE_EX}Goal{Style.RESET_ALL} {i+1}: " + ) + if ai_goal == "": + break + ai_goals.append(ai_goal) if not ai_goals: ai_goals = [ "Increase net worth", diff --git a/run.sh b/run.sh index 287499f8f74..29ded78a650 100755 --- a/run.sh +++ b/run.sh @@ -22,7 +22,7 @@ if $PYTHON_CMD -c "import sys; sys.exit(sys.version_info < (3, 10))"; then echo Installing missing packages... $PYTHON_CMD -m pip install -r requirements.txt fi - $PYTHON_CMD -m autogpt $@ + $PYTHON_CMD -m autogpt "$@" read -p "Press any key to continue..." else echo "Python 3.10 or higher is required to run Auto GPT." diff --git a/tests/unit/data/test_ai_config.yaml b/tests/unit/data/test_ai_config.yaml new file mode 100644 index 00000000000..b6bc7cd9408 --- /dev/null +++ b/tests/unit/data/test_ai_config.yaml @@ -0,0 +1,5 @@ +ai_goals: +- Test goal 1 +ai_name: testGPT +ai_role: testRole +api_budget: 1.0 \ No newline at end of file