From eac5c1f6e6be1c068e74fd6b153e5fc86456b5a7 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sun, 2 Apr 2023 19:03:37 +0200 Subject: [PATCH 001/130] Add documentation --- scripts/agent_manager.py | 4 ++++ scripts/ai_functions.py | 5 ++++- scripts/browse.py | 6 ++++++ scripts/chat.py | 1 + scripts/commands.py | 15 +++++++++++++++ scripts/config.py | 4 ++++ scripts/data.py | 1 + scripts/execute_code.py | 1 + scripts/file_operations.py | 5 +++++ scripts/keys.py | 1 + scripts/main.py | 5 +++++ scripts/speak.py | 1 + scripts/spinner.py | 5 +++++ 13 files changed, 53 insertions(+), 1 deletion(-) diff --git a/scripts/agent_manager.py b/scripts/agent_manager.py index 9939332b80..b829b21900 100644 --- a/scripts/agent_manager.py +++ b/scripts/agent_manager.py @@ -7,6 +7,7 @@ agents = {} # key, (task, full_message_history, model) def create_agent(task, prompt, model): + """Create a new agent and return its key""" global next_key global agents @@ -34,6 +35,7 @@ def create_agent(task, prompt, model): def message_agent(key, message): + """Send a message to an agent and return its response""" global agents task, messages, model = agents[int(key)] @@ -57,6 +59,7 @@ def message_agent(key, message): def list_agents(): + """Return a list of all agents""" global agents # Return a list of agent keys and their tasks @@ -64,6 +67,7 @@ def list_agents(): def delete_agent(key): + """Delete an agent and return True if successful, False otherwise""" global agents try: diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index b79c4dbce1..6659bb5f00 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -6,7 +6,7 @@ import openai # This is a magic function that can do anything with no-code. See # https://github.com/Torantulino/AI-Functions for more info. def call_ai_function(function, args, description, model="gpt-4"): - # parse args to comma seperated string + """Calls an AI function and returns the result.""" args = ", ".join(args) messages = [ { @@ -27,6 +27,7 @@ def call_ai_function(function, args, description, model="gpt-4"): def evaluate_code(code: str) -> List[str]: + """Evaluates the given code and returns a list of suggestions for improvements.""" function_string = "def analyze_code(code: str) -> List[str]:" args = [code] description_string = """Analyzes the given code and returns a list of suggestions for improvements.""" @@ -39,6 +40,7 @@ def evaluate_code(code: str) -> List[str]: def improve_code(suggestions: List[str], code: str) -> str: + """Improves the provided code based on the suggestions provided, making no other changes.""" function_string = ( "def generate_improved_code(suggestions: List[str], code: str) -> str:" ) @@ -53,6 +55,7 @@ def improve_code(suggestions: List[str], code: str) -> str: def write_tests(code: str, focus: List[str]) -> str: + """Generates test cases for the existing code, focusing on specific areas if required.""" function_string = ( "def create_test_cases(code: str, focus: Optional[str] = None) -> str:" ) diff --git a/scripts/browse.py b/scripts/browse.py index af7ce57633..74d01b809e 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -6,6 +6,7 @@ import openai def scrape_text(url): + """Scrape text from a webpage""" response = requests.get(url) # Check if the response contains an HTTP error @@ -26,6 +27,7 @@ def scrape_text(url): def extract_hyperlinks(soup): + """Extract hyperlinks from a BeautifulSoup object""" hyperlinks = [] for link in soup.find_all('a', href=True): hyperlinks.append((link.text, link['href'])) @@ -33,6 +35,7 @@ def extract_hyperlinks(soup): def format_hyperlinks(hyperlinks): + """Format hyperlinks into a list of strings""" formatted_links = [] for link_text, link_url in hyperlinks: formatted_links.append(f"{link_text} ({link_url})") @@ -40,6 +43,7 @@ def format_hyperlinks(hyperlinks): def scrape_links(url): + """Scrape hyperlinks from a webpage""" response = requests.get(url) # Check if the response contains an HTTP error @@ -57,6 +61,7 @@ def scrape_links(url): def split_text(text, max_length=8192): + """Split text into chunks of a maximum length""" paragraphs = text.split("\n") current_length = 0 current_chunk = [] @@ -75,6 +80,7 @@ def split_text(text, max_length=8192): def summarize_text(text, is_website=True): + """Summarize text using GPT-3""" if text == "": return "Error: No text to summarize" diff --git a/scripts/chat.py b/scripts/chat.py index a406812d7a..56f98240e6 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -27,6 +27,7 @@ def chat_with_ai( permanent_memory, token_limit, debug=False): + """Interact with the OpenAI API, sending the prompt, user input, message history, and permanent memory.""" while True: try: """ diff --git a/scripts/commands.py b/scripts/commands.py index 610856321b..dbfe36c73d 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -12,6 +12,7 @@ cfg = Config() def get_command(response): + """Parse the response and return the command name and arguments""" try: response_json = json.loads(response) command = response_json["command"] @@ -30,6 +31,7 @@ def get_command(response): def execute_command(command_name, arguments): + """Execute the command and return the response""" try: if command_name == "google": return google_search(arguments["input"]) @@ -93,11 +95,13 @@ def execute_command(command_name, arguments): def get_datetime(): + """Return the current date and time""" return "Current date and time: " + \ datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") def google_search(query, num_results=8): + """Return the results of a google search""" search_results = [] for j in browse.search(query, num_results=num_results): search_results.append(j) @@ -106,6 +110,7 @@ def google_search(query, num_results=8): def browse_website(url): + """Return the results of a google search""" summary = get_text_summary(url) links = get_hyperlinks(url) @@ -119,23 +124,27 @@ def browse_website(url): def get_text_summary(url): + """Return the results of a google search""" text = browse.scrape_text(url) summary = browse.summarize_text(text) return """ "Result" : """ + summary def get_hyperlinks(url): + """Return the results of a google search""" link_list = browse.scrape_links(url) return link_list def commit_memory(string): + """Commit a string to memory""" _text = f"""Committing memory with string "{string}" """ mem.permanent_memory.append(string) return _text def delete_memory(key): + """Delete a memory with a given key""" if key >= 0 and key < len(mem.permanent_memory): _text = "Deleting memory with key " + str(key) del mem.permanent_memory[key] @@ -147,6 +156,7 @@ def delete_memory(key): def overwrite_memory(key, string): + """Overwrite a memory with a given key""" if key >= 0 and key < len(mem.permanent_memory): _text = "Overwriting memory with key " + \ str(key) + " and string " + string @@ -159,11 +169,13 @@ def overwrite_memory(key, string): def shutdown(): + """Shut down the program""" print("Shutting down...") quit() def start_agent(name, task, prompt, model="gpt-3.5-turbo"): + """Start an agent with a given name, task, and prompt""" global cfg # Remove underscores from name @@ -187,6 +199,7 @@ def start_agent(name, task, prompt, model="gpt-3.5-turbo"): def message_agent(key, message): + """Message an agent with a given key and message""" global cfg agent_response = agents.message_agent(key, message) @@ -198,10 +211,12 @@ def message_agent(key, message): def list_agents(): + """List all agents""" return agents.list_agents() def delete_agent(key): + """Delete an agent with a given key""" result = agents.delete_agent(key) if not result: return f"Agent {key} does not exist." diff --git a/scripts/config.py b/scripts/config.py index bc7ebf7180..ac14565779 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -6,6 +6,7 @@ class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): + """Call method for the singleton metaclass.""" if cls not in cls._instances: cls._instances[cls] = super( Singleton, cls).__call__( @@ -19,11 +20,14 @@ class Config(metaclass=Singleton): """ def __init__(self): + """Initialize the configuration class.""" self.continuous_mode = False self.speak_mode = False def set_continuous_mode(self, value: bool): + """Set the continuous mode value.""" self.continuous_mode = value def set_speak_mode(self, value: bool): + """Set the speak mode value.""" self.speak_mode = value diff --git a/scripts/data.py b/scripts/data.py index 194735578e..b1faec8757 100644 --- a/scripts/data.py +++ b/scripts/data.py @@ -1,4 +1,5 @@ def load_prompt(): + """Load the prompt from data/prompt.txt""" try: # Load the promt from data/prompt.txt with open("data/prompt.txt", "r") as prompt_file: diff --git a/scripts/execute_code.py b/scripts/execute_code.py index cfd818d49b..01b454a09f 100644 --- a/scripts/execute_code.py +++ b/scripts/execute_code.py @@ -3,6 +3,7 @@ import os def execute_python_file(file): + """Execute a Python file in a Docker container and return the output""" workspace_folder = "auto_gpt_workspace" if not file.endswith(".py"): diff --git a/scripts/file_operations.py b/scripts/file_operations.py index 62b3dc4b7d..d92709fb46 100644 --- a/scripts/file_operations.py +++ b/scripts/file_operations.py @@ -9,6 +9,7 @@ if not os.path.exists(working_directory): def safe_join(base, *paths): + """Join one or more path components intelligently.""" new_path = os.path.join(base, *paths) norm_new_path = os.path.normpath(new_path) @@ -19,6 +20,7 @@ def safe_join(base, *paths): def read_file(filename): + """Read a file and return the contents""" try: filepath = safe_join(working_directory, filename) with open(filepath, "r") as f: @@ -29,6 +31,7 @@ def read_file(filename): def write_to_file(filename, text): + """Write text to a file""" try: filepath = safe_join(working_directory, filename) with open(filepath, "w") as f: @@ -39,6 +42,7 @@ def write_to_file(filename, text): def append_to_file(filename, text): + """Append text to a file""" try: filepath = safe_join(working_directory, filename) with open(filepath, "a") as f: @@ -49,6 +53,7 @@ def append_to_file(filename, text): def delete_file(filename): + """Delete a file""" try: filepath = safe_join(working_directory, filename) os.remove(filepath) diff --git a/scripts/keys.py b/scripts/keys.py index 1cd439cdd6..dbb3b91a50 100644 --- a/scripts/keys.py +++ b/scripts/keys.py @@ -1,3 +1,4 @@ +# This file contains the API keys for the various APIs used in the project. # Get yours from: https://beta.openai.com/account/api-keys OPENAI_API_KEY = "YOUR-OPENAI-KEY" # To access your ElevenLabs API key, head to https://elevenlabs.io, you diff --git a/scripts/main.py b/scripts/main.py index d51ad0fc91..298fa95ccc 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -14,6 +14,7 @@ from config import Config class Argument(Enum): + """This class is used to define the different arguments that can be passed""" CONTINUOUS_MODE = "continuous-mode" SPEAK_MODE = "speak-mode" @@ -25,6 +26,7 @@ def print_to_console( speak_text=False, min_typing_speed=0.05, max_typing_speed=0.01): + """Prints text to the console with a typing effect""" global cfg if speak_text and cfg.speak_mode: speak.say_text(f"{title}. {content}") @@ -44,6 +46,7 @@ def print_to_console( def print_assistant_thoughts(assistant_reply): + """Prints the assistant's thoughts to the console""" global ai_name global cfg try: @@ -102,6 +105,7 @@ def print_assistant_thoughts(assistant_reply): def construct_prompt(): + """Constructs the prompt for the AI""" global ai_name # Construct the prompt print_to_console( @@ -165,6 +169,7 @@ def construct_prompt(): def parse_arguments(): + """Parses the arguments passed to the script""" global cfg cfg.set_continuous_mode(False) cfg.set_speak_mode(False) diff --git a/scripts/speak.py b/scripts/speak.py index 13ceb8d904..db05a024fe 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -12,6 +12,7 @@ tts_headers = { def say_text(text, voice_index=0): + """Say text using ElevenLabs API""" tts_url = "https://api.elevenlabs.io/v1/text-to-speech/{voice_id}".format( voice_id=voices[voice_index]) diff --git a/scripts/spinner.py b/scripts/spinner.py index 2a48dfecfd..df39dbbd22 100644 --- a/scripts/spinner.py +++ b/scripts/spinner.py @@ -5,7 +5,9 @@ import time class Spinner: + """A simple spinner class""" def __init__(self, message="Loading...", delay=0.1): + """Initialize the spinner class""" self.spinner = itertools.cycle(['-', '/', '|', '\\']) self.delay = delay self.message = message @@ -13,6 +15,7 @@ class Spinner: self.spinner_thread = None def spin(self): + """Spin the spinner""" while self.running: sys.stdout.write(next(self.spinner) + " " + self.message + "\r") sys.stdout.flush() @@ -20,11 +23,13 @@ class Spinner: sys.stdout.write('\b' * (len(self.message) + 2)) def __enter__(self): + """Start the spinner""" self.running = True self.spinner_thread = threading.Thread(target=self.spin) self.spinner_thread.start() def __exit__(self, exc_type, exc_value, exc_traceback): + """Stop the spinner""" self.running = False self.spinner_thread.join() sys.stdout.write('\r' + ' ' * (len(self.message) + 2) + '\r') From 5c02bfa4de5b2ef4aa3bac2a10a3b1d10f0fca9f Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Mon, 3 Apr 2023 13:38:56 +0200 Subject: [PATCH 002/130] Update .gitignore Ignore venv folder --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4d0844458a..d47b839663 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ scripts/__pycache__/keys.cpython-310.pyc package-lock.json *.pyc scripts/auto_gpt_workspace/* -*.mpeg \ No newline at end of file +*.mpeg +venv/* \ No newline at end of file From 765210f0cd7a40cac8d82be55b4543fa1b17e388 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Mon, 3 Apr 2023 14:10:02 +0200 Subject: [PATCH 003/130] Add extra documentation --- scripts/ai_config.py | 6 +++++- scripts/call_ai_function.py | 1 + scripts/config.py | 6 ++++++ scripts/file_operations.py | 2 +- scripts/json_parser.py | 2 ++ scripts/llm_utils.py | 1 + scripts/main.py | 3 ++- 7 files changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 945fcfb233..42d227d019 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -3,7 +3,9 @@ import data class AIConfig: + """Class to store the AI's name, role, and goals.""" def __init__(self, ai_name="", ai_role="", ai_goals=[]): + """Initialize the AIConfig class""" self.ai_name = ai_name self.ai_role = ai_role self.ai_goals = ai_goals @@ -13,7 +15,7 @@ class AIConfig: @classmethod def load(cls, config_file=SAVE_FILE): - # Load variables from yaml file if it exists + """Load variables from yaml file if it exists, otherwise use defaults.""" try: with open(config_file) as file: config_params = yaml.load(file, Loader=yaml.FullLoader) @@ -27,11 +29,13 @@ class AIConfig: return cls(ai_name, ai_role, ai_goals) def save(self, config_file=SAVE_FILE): + """Save variables to yaml file.""" config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals} with open(config_file, "w") as file: yaml.dump(config, file) def construct_full_prompt(self): + """Construct the full prompt for the AI to use.""" prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.""" # Construct full prompt diff --git a/scripts/call_ai_function.py b/scripts/call_ai_function.py index 0c864b4909..83c876876c 100644 --- a/scripts/call_ai_function.py +++ b/scripts/call_ai_function.py @@ -6,6 +6,7 @@ from llm_utils import create_chat_completion # This is a magic function that can do anything with no-code. See # https://github.com/Torantulino/AI-Functions for more info. def call_ai_function(function, args, description, model=cfg.smart_llm_model): + """Call an AI function with the given args and description.""" # For each arg, if any are None, convert to "None": args = [str(arg) if arg is not None else "None" for arg in args] # parse args to comma seperated string diff --git a/scripts/config.py b/scripts/config.py index fa3bf7ccd2..d98cb69891 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -51,21 +51,27 @@ class Config(metaclass=Singleton): self.speak_mode = value def set_fast_llm_model(self, value: str): + """Set the fast LLM model value.""" self.fast_llm_model = value def set_smart_llm_model(self, value: str): + """Set the smart LLM model value.""" self.smart_llm_model = value def set_fast_token_limit(self, value: int): + """Set the fast token limit value.""" self.fast_token_limit = value def set_smart_token_limit(self, value: int): + """Set the smart token limit value.""" self.smart_token_limit = value def set_openai_api_key(self, value: str): + """Set the OpenAI API key value.""" self.apiopenai_api_key_key = value def set_elevenlabs_api_key(self, value: str): + """Set the ElevenLabs API key value.""" self.elevenlabs_api_key = value diff --git a/scripts/file_operations.py b/scripts/file_operations.py index d92709fb46..f58d2cbcb5 100644 --- a/scripts/file_operations.py +++ b/scripts/file_operations.py @@ -3,7 +3,7 @@ import os.path # Set a dedicated folder for file I/O working_directory = "auto_gpt_workspace" - +# Create the directory if it doesn't exist if not os.path.exists(working_directory): os.makedirs(working_directory) diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 8154b584a1..edd6730560 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -4,6 +4,7 @@ from config import Config cfg = Config() def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): + """Fix and parse the given JSON string.""" json_schema = """ { "command": { @@ -50,6 +51,7 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): # TODO: Make debug a global config var def fix_json(json_str: str, schema: str, debug=False) -> str: + """Fix the given JSON string to make it parseable and fully complient with the provided schema.""" # Try to fix the JSON using gpt: function_string = "def fix_json(json_str: str, schema:str=None) -> str:" args = [json_str, schema] diff --git a/scripts/llm_utils.py b/scripts/llm_utils.py index 41f396250f..c512e99733 100644 --- a/scripts/llm_utils.py +++ b/scripts/llm_utils.py @@ -6,6 +6,7 @@ openai.api_key = cfg.openai_api_key # Overly simple abstraction until we create something better def create_chat_completion(messages, model=None, temperature=None, max_tokens=None)->str: + """Create a chat completion using the OpenAI API.""" response = openai.ChatCompletion.create( model=model, messages=messages, diff --git a/scripts/main.py b/scripts/main.py index b4236c52a0..b0aef17874 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -117,7 +117,7 @@ def print_assistant_thoughts(assistant_reply): print_to_console("Error: \n", Fore.RED, call_stack) def load_variables(config_file="config.yaml"): - # Load variables from yaml file if it exists + """Load variables from yaml file if it exists, otherwise prompt the user for input""" try: with open(config_file) as file: config = yaml.load(file, Loader=yaml.FullLoader) @@ -200,6 +200,7 @@ Continue (y/n): """) def prompt_user(): + """Prompt the user for input""" ai_name = "" # Construct the prompt print_to_console( From 5ced5cae3aad0e2a399e24e2671cc12e0f18a6c8 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Mon, 3 Apr 2023 14:10:15 +0200 Subject: [PATCH 004/130] Update Dockerfile The requirements file wasn't on the file system. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c67291c88d..d2a127c7f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.11 WORKDIR /app COPY scripts/ /app - +COPY requirements.txt /app/requirements.txt RUN pip install -r requirements.txt CMD ["python", "main.py"] From 301ff85fd57dd018b7446798b924073cbee3eaf1 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 00:15:17 +0200 Subject: [PATCH 005/130] Update main.py Introduce check to OPENAI_API_KEY --- scripts/main.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index 4e79974369..e334753f37 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -17,6 +17,16 @@ import traceback import yaml import argparse +def check_openai_api_key(): + """Check if the OpenAI API key is set in config.py or as an environment variable.""" + if not cfg.openai_api_key: + print( + Fore.RED + + "Please set your OpenAI API key in config.py or as an environment variable." + ) + print("You can get your key from https://beta.openai.com/account/api-keys") + exit(1) + def print_to_console( title, @@ -275,7 +285,7 @@ def parse_arguments(): # TODO: fill in llm values here - +check_openai_api_key() cfg = Config() parse_arguments() ai_name = "" From c74ad984cfa755031a85296c45a8a8d77e0b8906 Mon Sep 17 00:00:00 2001 From: Malik M Alnakhaleh Date: Mon, 3 Apr 2023 20:54:12 -0400 Subject: [PATCH 006/130] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5848c585d9..284267dbf8 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ python scripts/main.py --speak ## πŸ” Google API Keys Configuration -This section is optional, use the official google api if you are having issues with error 429 when running google search. +This section is optional, use the official google api if you are having issues with error 429 when running a google search. To use the `google_official_search` command, you need to set up your Google API keys in your environment variables. 1. Go to the [Google Cloud Console](https://console.cloud.google.com/). From b6344b98e241eb2d9607a54d9a4d9efc09f7bb8f Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:46:02 +0200 Subject: [PATCH 007/130] Update main.py Add separation between code blocks. --- scripts/main.py | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index 0ec8978fc4..8f0400ae9b 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -27,17 +27,24 @@ def print_to_console( max_typing_speed=0.01): """Prints text to the console with a typing effect""" global cfg + if speak_text and cfg.speak_mode: speak.say_text(f"{title}. {content}") + print(title_color + title + " " + Style.RESET_ALL, end="") + if content: + if isinstance(content, list): content = " ".join(content) words = content.split() + for i, word in enumerate(words): print(word, end="", flush=True) + if i < len(words) - 1: print(" ", end="", flush=True) + typing_speed = random.uniform(min_typing_speed, max_typing_speed) time.sleep(typing_speed) # type faster after each word @@ -73,6 +80,7 @@ def print_assistant_thoughts(assistant_reply): if assistant_thoughts_plan: print_to_console("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): @@ -80,6 +88,7 @@ def print_assistant_thoughts(assistant_reply): # Split the input_string using the newline character and dashes lines = assistant_thoughts_plan.split('\n') + for line in lines: line = line.lstrip("- ") print_to_console("- ", Fore.GREEN, line.strip()) @@ -114,11 +123,13 @@ def load_variables(config_file="config.yaml"): # Prompt the user for input if config file is missing or empty values if not ai_name: ai_name = input("Name your AI: ") + if ai_name == "": ai_name = "Entrepreneur-GPT" if not ai_role: ai_role = input(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." @@ -127,16 +138,20 @@ def load_variables(config_file="config.yaml"): print("For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") print("Enter nothing to load defaults, enter nothing when finished.") ai_goals = [] + for i in range(5): ai_goal = input(f"Goal {i+1}: ") + if ai_goal == "": break ai_goals.append(ai_goal) + if len(ai_goals) == 0: ai_goals = ["Increase net worth", "Grow Twitter Account", "Develop and manage multiple businesses autonomously"] # Save variables to yaml file config = {"ai_name": ai_name, "ai_role": ai_role, "ai_goals": ai_goals} + with open(config_file, "w") as file: documents = yaml.dump(config, file) @@ -145,6 +160,7 @@ def load_variables(config_file="config.yaml"): # Construct full prompt full_prompt = f"You are {ai_name}, {ai_role}\n{prompt_start}\n\nGOALS:\n\n" + for i, goal in enumerate(ai_goals): full_prompt += f"{i+1}. {goal}\n" @@ -155,6 +171,7 @@ def load_variables(config_file="config.yaml"): def construct_prompt(): """Construct the prompt for the AI to respond to""" config = AIConfig.load() + if config.ai_name: print_to_console( f"Welcome back! ", @@ -162,10 +179,11 @@ def construct_prompt(): f"Would you like me to return to being {config.ai_name}?", speak_text=True) should_continue = input(f"""Continue with the last settings? -Name: {config.ai_name} -Role: {config.ai_role} -Goals: {config.ai_goals} -Continue (y/n): """) + Name: {config.ai_name} + Role: {config.ai_role} + Goals: {config.ai_goals} + Continue (y/n): """) + if should_continue.lower() == "n": config = AIConfig() @@ -196,7 +214,9 @@ def prompt_user(): "Name your AI: ", Fore.GREEN, "For example, 'Entrepreneur-GPT'") + ai_name = input("AI Name: ") + if ai_name == "": ai_name = "Entrepreneur-GPT" @@ -212,6 +232,7 @@ def prompt_user(): Fore.GREEN, "For example, 'an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth.'") ai_role = input(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." @@ -220,13 +241,18 @@ def prompt_user(): "Enter up to 5 goals for your AI: ", Fore.GREEN, "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") + print("Enter nothing to load defaults, enter nothing when finished.", flush=True) ai_goals = [] + for i in range(5): ai_goal = input(f"{Fore.LIGHTBLUE_EX}Goal{Style.RESET_ALL} {i+1}: ") + if ai_goal == "": break + ai_goals.append(ai_goal) + if len(ai_goals) == 0: ai_goals = ["Increase net worth", "Grow Twitter Account", "Develop and manage multiple businesses autonomously"] @@ -234,6 +260,7 @@ def prompt_user(): config = AIConfig(ai_name, ai_role, ai_goals) return config + def parse_arguments(): """Parses the arguments passed to the script""" global cfg @@ -307,11 +334,11 @@ while True: "NEXT ACTION: ", Fore.CYAN, f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}") - print( - "Enter 'y' to authorise command or 'n' to exit program...", - flush=True) + print("Enter 'y' to authorise command or 'n' to exit program...", flush=True) + while True: console_input = input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) + if console_input.lower() == "y": user_input = "GENERATE NEXT COMMAND JSON" break From cd164648ba8a9368bde00d5812a96ed569ea0d76 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:47:04 +0200 Subject: [PATCH 008/130] Update ai_config.py Add separation between code blocks. --- scripts/ai_config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 42d227d019..0b521b7dce 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -13,6 +13,7 @@ class AIConfig: # Soon this will go in a folder where it remembers more stuff about the run(s) SAVE_FILE = "last_run_ai_settings.yaml" + @classmethod def load(cls, config_file=SAVE_FILE): """Load variables from yaml file if it exists, otherwise use defaults.""" @@ -28,18 +29,21 @@ class AIConfig: return cls(ai_name, ai_role, ai_goals) + def save(self, config_file=SAVE_FILE): """Save variables to yaml file.""" config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals} with open(config_file, "w") as file: yaml.dump(config, file) + def construct_full_prompt(self): """Construct the full prompt for the AI to use.""" prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.""" # Construct full prompt full_prompt = f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n" + for i, goal in enumerate(self.ai_goals): full_prompt += f"{i+1}. {goal}\n" From 27bf7c47e98a3c8fbb660b6fa00f6d61771863d4 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:48:19 +0200 Subject: [PATCH 009/130] Update ai_functions.py Unnecessary spaces --- scripts/ai_functions.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index eb524b9b7c..f93d7ea693 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -6,7 +6,6 @@ from json_parser import fix_and_parse_json cfg = Config() # Evaluating code - def evaluate_code(code: str) -> List[str]: """Evaluates the given code and returns a list of suggestions for improvements.""" function_string = "def analyze_code(code: str) -> List[str]:" @@ -19,7 +18,6 @@ def evaluate_code(code: str) -> List[str]: # Improving code - def improve_code(suggestions: List[str], code: str) -> str: """Improves the provided code based on the suggestions provided, making no other changes.""" function_string = ( @@ -33,8 +31,6 @@ def improve_code(suggestions: List[str], code: str) -> str: # Writing tests - - def write_tests(code: str, focus: List[str]) -> str: """Generates test cases for the existing code, focusing on specific areas if required.""" function_string = ( From 1b4a5301d0d4406732b8e903af1bfda8e7552a6d Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:49:28 +0200 Subject: [PATCH 010/130] Update ai_functions.py Just import dumps from JSON library. --- scripts/ai_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index f93d7ea693..eb6dbd935c 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -1,5 +1,5 @@ from typing import List, Optional -import json +from json import dumps from config import Config from call_ai_function import call_ai_function from json_parser import fix_and_parse_json @@ -23,7 +23,7 @@ def improve_code(suggestions: List[str], code: str) -> str: function_string = ( "def generate_improved_code(suggestions: List[str], code: str) -> str:" ) - args = [json.dumps(suggestions), code] + args = [dumps(suggestions), code] description_string = """Improves the provided code based on the suggestions provided, making no other changes.""" result_string = call_ai_function(function_string, args, description_string) @@ -36,7 +36,7 @@ def write_tests(code: str, focus: List[str]) -> str: function_string = ( "def create_test_cases(code: str, focus: Optional[str] = None) -> str:" ) - args = [code, json.dumps(focus)] + args = [code, dumps(focus)] description_string = """Generates test cases for the existing code, focusing on specific areas if required.""" result_string = call_ai_function(function_string, args, description_string) From 39f758ba5ceaac927c50f3bacb94e4b24c5f0706 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:53:47 +0200 Subject: [PATCH 011/130] Update browse.py Add separation between code blocks and standardize dictionary definition. --- scripts/browse.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/browse.py b/scripts/browse.py index 8a10ddd603..85f7bc6cff 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -5,6 +5,7 @@ from llm_utils import create_chat_completion cfg = Config() + def scrape_text(url): """Scrape text from a webpage""" response = requests.get(url) @@ -29,16 +30,20 @@ def scrape_text(url): def extract_hyperlinks(soup): """Extract hyperlinks from a BeautifulSoup object""" hyperlinks = [] + for link in soup.find_all('a', href=True): hyperlinks.append((link.text, link['href'])) + return hyperlinks def format_hyperlinks(hyperlinks): """Format hyperlinks into a list of strings""" formatted_links = [] + for link_text, link_url in hyperlinks: formatted_links.append(f"{link_text} ({link_url})") + return formatted_links @@ -67,6 +72,7 @@ def split_text(text, max_length=8192): current_chunk = [] for paragraph in paragraphs: + if current_length + len(paragraph) + 1 <= max_length: current_chunk.append(paragraph) current_length += len(paragraph) + 1 @@ -90,19 +96,22 @@ def summarize_text(text, is_website=True): for i, chunk in enumerate(chunks): print("Summarizing chunk " + str(i + 1) + " / " + str(len(chunks))) + if is_website: messages = [ { "role": "user", "content": "Please summarize the following website text, do not describe the general website, but instead concisely extract the specific information this subpage contains.: " + - chunk}, + chunk + } ] else: messages = [ { "role": "user", "content": "Please summarize the following text, focusing on extracting concise and specific information: " + - chunk}, + chunk + } ] summary = create_chat_completion( @@ -121,14 +130,16 @@ def summarize_text(text, is_website=True): { "role": "user", "content": "Please summarize the following website text, do not describe the general website, but instead concisely extract the specific information this subpage contains.: " + - combined_summary}, + combined_summary + } ] else: messages = [ { "role": "user", "content": "Please summarize the following text, focusing on extracting concise and specific infomation: " + - combined_summary}, + combined_summary + } ] final_summary = create_chat_completion( From 772d4beb218e66b38347a265640d15dc8bc304b6 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:54:52 +0200 Subject: [PATCH 012/130] Update browse.py Just import GET from requests library. --- scripts/browse.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/browse.py b/scripts/browse.py index 85f7bc6cff..20ffc03527 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -1,4 +1,4 @@ -import requests +from requests import get from bs4 import BeautifulSoup from config import Config from llm_utils import create_chat_completion @@ -8,7 +8,7 @@ cfg = Config() def scrape_text(url): """Scrape text from a webpage""" - response = requests.get(url) + response = get(url) # Check if the response contains an HTTP error if response.status_code >= 400: @@ -49,7 +49,7 @@ def format_hyperlinks(hyperlinks): def scrape_links(url): """Scrape hyperlinks from a webpage""" - response = requests.get(url) + response = get(url) # Check if the response contains an HTTP error if response.status_code >= 400: From 1b7b367ce9e0455caf7ec2efbb0b5f4701bd2a51 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:56:56 +0200 Subject: [PATCH 013/130] Update call_ai_function.py Standardize importation of libraries to top. --- scripts/call_ai_function.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/call_ai_function.py b/scripts/call_ai_function.py index 83c876876c..44f9fd8cac 100644 --- a/scripts/call_ai_function.py +++ b/scripts/call_ai_function.py @@ -1,7 +1,7 @@ from config import Config +from llm_utils import create_chat_completion cfg = Config() -from llm_utils import create_chat_completion # This is a magic function that can do anything with no-code. See # https://github.com/Torantulino/AI-Functions for more info. From 238824553ae8626af1e8e27218d947a859a220ac Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 10:58:38 +0200 Subject: [PATCH 014/130] Update chat.py Standardize importation of libraries to top. --- scripts/chat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/chat.py b/scripts/chat.py index c526be15b2..819e08815c 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -3,10 +3,8 @@ import openai from dotenv import load_dotenv from config import Config import token_counter - -cfg = Config() - from llm_utils import create_chat_completion +cfg = Config() def create_chat_message(role, content): @@ -50,8 +48,10 @@ def chat_with_ai( """ model = cfg.fast_llm_model # TODO: Change model from hardcode to argument # Reserve 1000 tokens for the response + if debug: print(f"Token limit: {token_limit}") + send_token_limit = token_limit - 1000 current_context = [ From 500b2b48367566708829a9f5ce4eb4c4d9c4acde Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:02:01 +0200 Subject: [PATCH 015/130] Update chat.py Adds code separation between code blocks. --- scripts/chat.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/chat.py b/scripts/chat.py index 819e08815c..dd52e784ee 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -73,6 +73,7 @@ def chat_with_ai( message_to_add = full_message_history[next_message_to_add_index] tokens_to_add = token_counter.count_message_tokens([message_to_add], model) + if current_tokens_used + tokens_to_add > send_token_limit: break @@ -98,13 +99,16 @@ def chat_with_ai( print(f"Send Token Count: {current_tokens_used}") print(f"Tokens remaining for response: {tokens_remaining}") print("------------ CONTEXT SENT TO AI ---------------") + for message in current_context: # Skip printing the prompt + if message["role"] == "system" and message["content"] == prompt: continue print( f"{message['role'].capitalize()}: {message['content']}") print() + print("----------- END OF CONTEXT ----------------") # TODO: use a model defined elsewhere, so that model can contain temperature and other settings we care about From 9699a1ca78a18188c45ba09bc6b07f69624bd8bc Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:03:09 +0200 Subject: [PATCH 016/130] Update chat.py Just import sleep from time library. --- scripts/chat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/chat.py b/scripts/chat.py index dd52e784ee..17e36879ed 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -1,4 +1,4 @@ -import time +from time import sleep import openai from dotenv import load_dotenv from config import Config @@ -130,4 +130,4 @@ def chat_with_ai( except openai.error.RateLimitError: # TODO: WHen we switch to langchain, this is built in print("Error: ", "API Rate Limit Reached. Waiting 10 seconds...") - time.sleep(10) + sleep(10) From 632d87c195ed52da45d4150c8d6eb64bbc96f243 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:28:15 +0200 Subject: [PATCH 017/130] Update commands.py Just import datetime from datetime library. --- scripts/commands.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/commands.py b/scripts/commands.py index 42fe1ebb57..7873c048d7 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,7 +1,7 @@ import browse import json import memory as mem -import datetime +from datetime import datetime import agent_manager as agents import speak from config import Config @@ -110,7 +110,7 @@ def execute_command(command_name, arguments): def get_datetime(): """Return the current date and time""" return "Current date and time: " + \ - datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + datetime.now().strftime("%Y-%m-%d %H:%M:%S") def google_search(query, num_results=8): @@ -122,6 +122,7 @@ def google_search(query, num_results=8): return json.dumps(search_results, ensure_ascii=False, indent=4) def google_official_search(query, num_results=8): + """Return the results of a google search using the official Google API""" from googleapiclient.discovery import build from googleapiclient.errors import HttpError import json From 8053ecd5c6f7c2ce7a3363acdb31063b8d91f5af Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:29:02 +0200 Subject: [PATCH 018/130] Update config.py Introduce spaces between code blocks. --- scripts/config.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index c7d04bf822..0650535f68 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -44,42 +44,52 @@ class Config(metaclass=Singleton): # Initialize the OpenAI API client openai.api_key = self.openai_api_key + def set_continuous_mode(self, value: bool): """Set the continuous mode value.""" self.continuous_mode = value + def set_speak_mode(self, value: bool): """Set the speak mode value.""" self.speak_mode = value + def set_fast_llm_model(self, value: str): """Set the fast LLM model value.""" self.fast_llm_model = value + def set_smart_llm_model(self, value: str): """Set the smart LLM model value.""" self.smart_llm_model = value + def set_fast_token_limit(self, value: int): """Set the fast token limit value.""" self.fast_token_limit = value + def set_smart_token_limit(self, value: int): """Set the smart token limit value.""" self.smart_token_limit = value + def set_openai_api_key(self, value: str): """Set the OpenAI API key value.""" self.openai_api_key = value - + + def set_elevenlabs_api_key(self, value: str): """Set the ElevenLabs API key value.""" self.elevenlabs_api_key = value - + + def set_google_api_key(self, value: str): """Set the Google API key value.""" self.google_api_key = value - + + def set_custom_search_engine_id(self, value: str): """Set the custom search engine ID value.""" - self.custom_search_engine_id = value \ No newline at end of file + self.custom_search_engine_id = value From 1632f7ebf6332642ceedd97587dad6f76bc46740 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:30:20 +0200 Subject: [PATCH 019/130] Update data.py Just import path from OS library. --- scripts/data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/data.py b/scripts/data.py index 2239384453..bb8936c11d 100644 --- a/scripts/data.py +++ b/scripts/data.py @@ -1,4 +1,4 @@ -import os +from os import path from pathlib import Path @@ -6,7 +6,7 @@ def load_prompt(): """Load the prompt from data/prompt.txt""" try: # get directory of this file: - file_dir = Path(os.path.dirname(os.path.realpath(__file__))) + file_dir = Path(path.dirname(path.realpath(__file__))) data_dir = file_dir / "data" prompt_file = data_dir / "prompt.txt" # Load the promt from data/prompt.txt From d450ac3a0b489bf820d2c3794b08955054d1f543 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:31:20 +0200 Subject: [PATCH 020/130] Update execute_code.py Just import path from OS library. --- scripts/execute_code.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/execute_code.py b/scripts/execute_code.py index f34469dda8..22ae104746 100644 --- a/scripts/execute_code.py +++ b/scripts/execute_code.py @@ -1,5 +1,5 @@ import docker -import os +from os import path def execute_python_file(file): @@ -11,9 +11,9 @@ def execute_python_file(file): if not file.endswith(".py"): return "Error: Invalid file type. Only .py files are allowed." - file_path = os.path.join(workspace_folder, file) + file_path = path.join(workspace_folder, file) - if not os.path.isfile(file_path): + if not path.isfile(file_path): return f"Error: File '{file}' does not exist." try: From 1d10236a63e4535a7a16aa01f500d9932760f651 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:32:55 +0200 Subject: [PATCH 021/130] Update file_operations.py Introduces spaces between code blocks. --- scripts/file_operations.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/file_operations.py b/scripts/file_operations.py index 65664e609d..cb0546168c 100644 --- a/scripts/file_operations.py +++ b/scripts/file_operations.py @@ -3,6 +3,7 @@ import os.path # Set a dedicated folder for file I/O working_directory = "auto_gpt_workspace" + # Create the directory if it doesn't exist if not os.path.exists(working_directory): os.makedirs(working_directory) @@ -35,8 +36,10 @@ def write_to_file(filename, text): try: filepath = safe_join(working_directory, filename) directory = os.path.dirname(filepath) + if not os.path.exists(directory): os.makedirs(directory) + with open(filepath, "w") as f: f.write(text) return "File written to successfully." From 621f18eba79f8a31aed2da88bc9b26007c21f1ec Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:34:28 +0200 Subject: [PATCH 022/130] Update json_parser.py Introduces spaces between code blocks. --- scripts/json_parser.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 5153f71b04..e827b75336 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -1,6 +1,7 @@ import json from call_ai_function import call_ai_function from config import Config + cfg = Config() def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): @@ -37,15 +38,18 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): json_str = json_str[:last_brace_index+1] return json.loads(json_str) except Exception as e: + if try_to_fix_with_gpt: print(f"Warning: Failed to parse AI output, attempting to fix.\n If you see this warning frequently, it's likely that your prompt is confusing the AI. Try changing it up slightly.") # Now try to fix this up using the ai_functions ai_fixed_json = fix_json(json_str, json_schema, False) + if ai_fixed_json != "failed": return json.loads(ai_fixed_json) else: print(f"Failed to fix ai output, telling the AI.") # This allows the AI to react to the error message, which usually results in it correcting its ways. return json_str + else: raise e @@ -59,15 +63,18 @@ def fix_json(json_str: str, schema: str, debug=False) -> str: # If it doesn't already start with a "`", add one: if not json_str.startswith("`"): json_str = "```json\n" + json_str + "\n```" + result_string = call_ai_function( function_string, args, description_string, model=cfg.fast_llm_model ) + if debug: print("------------ JSON FIX ATTEMPT ---------------") print(f"Original JSON: {json_str}") print("-----------") print(f"Fixed JSON: {result_string}") print("----------- END OF FIX ATTEMPT ----------------") + try: return json.loads(result_string) except: From 62615cacc9532bcae1fe94d4ef27e10610edd758 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Tue, 4 Apr 2023 11:36:50 +0200 Subject: [PATCH 023/130] Adds necessary spaces Introduces spaces between code blocks. --- scripts/llm_utils.py | 1 + scripts/speak.py | 4 ++-- scripts/spinner.py | 3 +++ scripts/token_counter.py | 6 ++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/llm_utils.py b/scripts/llm_utils.py index c512e99733..2981c8a0a2 100644 --- a/scripts/llm_utils.py +++ b/scripts/llm_utils.py @@ -1,5 +1,6 @@ import openai from config import Config + cfg = Config() openai.api_key = cfg.openai_api_key diff --git a/scripts/speak.py b/scripts/speak.py index 1e0eb4088a..5a0728a2c7 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -2,6 +2,7 @@ import os from playsound import playsound import requests from config import Config + cfg = Config() # TODO: Nicer names for these ids @@ -18,8 +19,7 @@ def say_text(text, voice_index=0): voice_id=voices[voice_index]) formatted_message = {"text": text} - response = requests.post( - tts_url, headers=tts_headers, json=formatted_message) + response = requests.post(tts_url, headers=tts_headers, json=formatted_message) if response.status_code == 200: with open("speech.mpeg", "wb") as f: diff --git a/scripts/spinner.py b/scripts/spinner.py index df39dbbd22..2b79ae262b 100644 --- a/scripts/spinner.py +++ b/scripts/spinner.py @@ -14,6 +14,7 @@ class Spinner: self.running = False self.spinner_thread = None + def spin(self): """Spin the spinner""" while self.running: @@ -22,12 +23,14 @@ class Spinner: time.sleep(self.delay) sys.stdout.write('\b' * (len(self.message) + 2)) + def __enter__(self): """Start the spinner""" self.running = True self.spinner_thread = threading.Thread(target=self.spin) self.spinner_thread.start() + def __exit__(self, exc_type, exc_value, exc_traceback): """Stop the spinner""" self.running = False diff --git a/scripts/token_counter.py b/scripts/token_counter.py index a28a9868ed..fc5b9c5118 100644 --- a/scripts/token_counter.py +++ b/scripts/token_counter.py @@ -1,6 +1,7 @@ import tiktoken from typing import List, Dict + def count_message_tokens(messages : List[Dict[str, str]], model : str = "gpt-3.5-turbo-0301") -> int: """ Returns the number of tokens used by a list of messages. @@ -17,6 +18,7 @@ def count_message_tokens(messages : List[Dict[str, str]], model : str = "gpt-3.5 except KeyError: print("Warning: model not found. Using cl100k_base encoding.") encoding = tiktoken.get_encoding("cl100k_base") + if model == "gpt-3.5-turbo": # !Node: gpt-3.5-turbo may change over time. Returning num tokens assuming gpt-3.5-turbo-0301.") return count_message_tokens(messages, model="gpt-3.5-turbo-0301") @@ -32,15 +34,19 @@ def count_message_tokens(messages : List[Dict[str, str]], model : str = "gpt-3.5 else: raise NotImplementedError(f"""num_tokens_from_messages() is not implemented for model {model}. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens.""") num_tokens = 0 + for message in messages: num_tokens += tokens_per_message + for key, value in message.items(): num_tokens += len(encoding.encode(value)) + if key == "name": num_tokens += tokens_per_name num_tokens += 3 # every reply is primed with <|start|>assistant<|message|> return num_tokens + def count_string_tokens(string: str, model_name: str) -> int: """ Returns the number of tokens in a text string. From 795fa3e1fe9da00f824f86faf2a62b4560932b4a Mon Sep 17 00:00:00 2001 From: Breval Le Floch Date: Tue, 4 Apr 2023 14:28:34 +0000 Subject: [PATCH 024/130] patch readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d66a602224..143846b482 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ git clone https://github.com/Torantulino/Auto-GPT.git 2. Navigate to the project directory: *(Type this into your CMD window, you're aiming to navigate the CMD window to the repository you just downloaded)* ``` -$ cd 'Auto-GPT' +cd 'Auto-GPT' ``` 3. Install the required dependencies: From 23ed4b3d2faee8a8b8854ad8b7f99e08462680ad Mon Sep 17 00:00:00 2001 From: Gershon Bialer Date: Tue, 4 Apr 2023 20:36:29 -0700 Subject: [PATCH 025/130] Make sure to copy over requirements.txt to Dockerfile. --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index c67291c88d..146a374717 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM python:3.11 WORKDIR /app COPY scripts/ /app +COPY requirements.txt /app RUN pip install -r requirements.txt From 32e20611dfa18b4dbea223876ef3bd8d49b1b28b Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Thu, 6 Apr 2023 12:26:07 +0100 Subject: [PATCH 026/130] small typo in Pinecone settings that referred to google --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7903129910..a89c5d03b7 100644 --- a/README.md +++ b/README.md @@ -152,14 +152,14 @@ are loaded for the agent at any given time. ### Setting up environment variables For Windows Users: ``` -setx PINECONE_API_KEY "YOUR_GOOGLE_API_KEY" -export PINECONE_ENV="Your region" # something like: us-east4-gcp +setx PINECONE_API_KEY "YOUR_PINECONE_API_KEY" +export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp ``` For macOS and Linux users: ``` -export PINECONE_API_KEY="YOUR_GOOGLE_API_KEY" -export PINECONE_ENV="Your region" # something like: us-east4-gcp +export PINECONE_API_KEY="YOUR_PINECONE_API_KEY" +export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp ``` From 3f106963a8b461a52ec1d9acb3a3273cac3ad6d7 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Thu, 6 Apr 2023 13:22:05 +0100 Subject: [PATCH 027/130] Changes playsound requirement to 1.2.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2efb371cc0..ce24709858 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ beautifulsoup4 colorama==0.4.6 openai==0.27.2 -playsound==1.3.0 +playsound==1.2.2 python-dotenv==1.0.0 pyyaml==6.0 readability-lxml==0.8.1 From 0573b2ade6cb44927d0702bdd276f01405ee33d9 Mon Sep 17 00:00:00 2001 From: DawgZter Date: Thu, 6 Apr 2023 23:17:42 +1000 Subject: [PATCH 028/130] Update Pinecone API Key Setup instructions for clarity Minor improvement in sentence clarity for Pinecone setup instructions. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a89c5d03b7..7e37490057 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,7 @@ export CUSTOM_SEARCH_ENGINE_ID="YOUR_CUSTOM_SEARCH_ENGINE_ID" ## 🌲 Pinecone API Key Setup -Pinecone enable a vector based memory so a vast memory can be stored and only relevant memories -are loaded for the agent at any given time. +Pinecone enables the storage of vast amounts of vector-based memory, allowing for only relevant memories to be loaded for the agent at any given time. 1. Go to app.pinecone.io and make an account if you don't already have one. 2. Choose the `Starter` plan to avoid being charged. From a090c089f17aa29752a4a5e0e0ab16ec549a789a Mon Sep 17 00:00:00 2001 From: Monkee1337 Date: Thu, 6 Apr 2023 15:30:11 +0200 Subject: [PATCH 029/130] changed order of pinecone explaination --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a89c5d03b7..f6d06f34e5 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,12 @@ are loaded for the agent at any given time. 3. Find your API key and region under the default project in the left sidebar. ### Setting up environment variables - For Windows Users: + +Simply set them in the `.env` file. + +Alternatively you can use the appropriate following method (advanced): + +For Windows Users: ``` setx PINECONE_API_KEY "YOUR_PINECONE_API_KEY" export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp @@ -163,7 +168,6 @@ export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp ``` -Or you can set them in the `.env` file. ## View Memory Usage From c1326469b206d7fb68eac0f753c925358c30675e Mon Sep 17 00:00:00 2001 From: Dino Hensen Date: Thu, 6 Apr 2023 17:15:40 +0200 Subject: [PATCH 030/130] The function default serialized gpt-4 value at import time, leading to yield a value of gpt-4 after setting gpt3only when not passing a value for model to the function when calling it, this fixes it --- scripts/call_ai_function.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/call_ai_function.py b/scripts/call_ai_function.py index 0c864b4909..db1c955683 100644 --- a/scripts/call_ai_function.py +++ b/scripts/call_ai_function.py @@ -5,7 +5,9 @@ from llm_utils import create_chat_completion # This is a magic function that can do anything with no-code. See # https://github.com/Torantulino/AI-Functions for more info. -def call_ai_function(function, args, description, model=cfg.smart_llm_model): +def call_ai_function(function, args, description, model=None): + if model is None: + model = cfg.smart_llm_model # For each arg, if any are None, convert to "None": args = [str(arg) if arg is not None else "None" for arg in args] # parse args to comma seperated string From f8e88a146a12754221517246d0a161e223db795a Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Thu, 6 Apr 2023 09:59:20 -0700 Subject: [PATCH 031/130] Create PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..6397f78c08 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ +### Background + + + +### Changes + + + +### Test Plan + + + +### Change Safety + +- [ ] I have added tests to cover my changes +- [ ] I have considered potential risks and mitigations for my changes + + From 85428df9fc1dd72bf5a959c92e37803fa9fcc8bc Mon Sep 17 00:00:00 2001 From: hunteraraujo Date: Thu, 6 Apr 2023 10:07:49 -0700 Subject: [PATCH 032/130] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6397f78c08..cb8ce34a1b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,7 +8,7 @@ ### Test Plan - + ### Change Safety From 1e9ac953a7395c756cee281e3f308e705442bfd2 Mon Sep 17 00:00:00 2001 From: Dr33dM3 <109325141+Dr33dM3@users.noreply.github.com> Date: Thu, 6 Apr 2023 10:43:29 -0700 Subject: [PATCH 033/130] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a89c5d03b7..226306739b 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ If you don't have access to the GPT4 api, this mode will allow you to use Auto-G ``` python scripts/main.py --gpt3only ``` +For your safety we recommend using a VM(Virtual Machine) so your device may not get bricked as continous mode can potenially be dangerous for your computer. ## ⚠️ Limitations This experiment aims to showcase the potential of GPT-4 but comes with some limitations: From 6819799ebe435e8e512bf07f1cd1d52f58a1b2f9 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Thu, 6 Apr 2023 22:25:17 -0500 Subject: [PATCH 034/130] Create an abstract MemoryProviderSingleton class. Pass config instead of instantiating a new one where used. --- scripts/commands.py | 4 +-- scripts/config.py | 7 ++++- scripts/main.py | 4 +-- scripts/memory/__init__.py | 0 scripts/memory/base.py | 34 +++++++++++++++++++++++ scripts/{memory.py => memory/pinecone.py} | 18 +++--------- scripts/memory/redismem.py | 0 7 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 scripts/memory/__init__.py create mode 100644 scripts/memory/base.py rename scripts/{memory.py => memory/pinecone.py} (80%) create mode 100644 scripts/memory/redismem.py diff --git a/scripts/commands.py b/scripts/commands.py index fc10d1d052..f00875f063 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,6 +1,6 @@ import browse import json -from memory import PineconeMemory +from memory.pinecone import PineconeMemory import datetime import agent_manager as agents import speak @@ -52,7 +52,7 @@ def get_command(response): def execute_command(command_name, arguments): - memory = PineconeMemory() + memory = PineconeMemory(cfg=cfg) try: if command_name == "google": diff --git a/scripts/config.py b/scripts/config.py index fe48d29800..1b716a3eba 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -1,3 +1,4 @@ +import abc import os import openai from dotenv import load_dotenv @@ -5,7 +6,7 @@ from dotenv import load_dotenv load_dotenv() -class Singleton(type): +class Singleton(abc.ABCMeta, type): """ Singleton metaclass for ensuring only one instance of a class. """ @@ -20,6 +21,10 @@ class Singleton(type): return cls._instances[cls] +class AbstractSingleton(abc.ABC, metaclass=Singleton): + pass + + class Config(metaclass=Singleton): """ Configuration class to store the state of bools for different scripts access. diff --git a/scripts/main.py b/scripts/main.py index a79fd553ce..acb63a39be 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -1,7 +1,7 @@ import json import random import commands as cmd -from memory import PineconeMemory +from memory.pinecone import PineconeMemory import data import chat from colorama import Fore, Style @@ -283,7 +283,7 @@ user_input = "Determine which next command to use, and respond using the format # Initialize memory and make sure it is empty. # this is particularly important for indexing and referencing pinecone memory -memory = PineconeMemory() +memory = PineconeMemory(cfg) memory.clear() print('Using memory of type: ' + memory.__class__.__name__) diff --git a/scripts/memory/__init__.py b/scripts/memory/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/memory/base.py b/scripts/memory/base.py new file mode 100644 index 0000000000..29f5d56be1 --- /dev/null +++ b/scripts/memory/base.py @@ -0,0 +1,34 @@ +import abc +from config import AbstractSingleton +import openai + + +def get_ada_embedding(text): + text = text.replace("\n", " ") + return openai.Embedding.create(input=[text], model="text-embedding-ada-002")["data"][0]["embedding"] + + +def get_text_from_embedding(embedding): + return openai.Embedding.retrieve(embedding, model="text-embedding-ada-002")["data"][0]["text"] + + +class MemoryProviderSingleton(AbstractSingleton): + @abc.abstractmethod + def add(self, data): + pass + + @abc.abstractmethod + def get(self, data): + pass + + @abc.abstractmethod + def clear(self): + pass + + @abc.abstractmethod + def get_relevant(self, data, num_relevant=5): + pass + + @abc.abstractmethod + def get_stats(self): + pass diff --git a/scripts/memory.py b/scripts/memory/pinecone.py similarity index 80% rename from scripts/memory.py rename to scripts/memory/pinecone.py index 0d265a31d8..8e1eaa570f 100644 --- a/scripts/memory.py +++ b/scripts/memory/pinecone.py @@ -1,21 +1,11 @@ -from config import Config, Singleton + import pinecone -import openai -cfg = Config() +from memory.base import MemoryProviderSingleton, get_ada_embedding -def get_ada_embedding(text): - text = text.replace("\n", " ") - return openai.Embedding.create(input=[text], model="text-embedding-ada-002")["data"][0]["embedding"] - - -def get_text_from_embedding(embedding): - return openai.Embedding.retrieve(embedding, model="text-embedding-ada-002")["data"][0]["text"] - - -class PineconeMemory(metaclass=Singleton): - def __init__(self): +class PineconeMemory(MemoryProviderSingleton): + def __init__(self, cfg): pinecone_api_key = cfg.pinecone_api_key pinecone_region = cfg.pinecone_region pinecone.init(api_key=pinecone_api_key, environment=pinecone_region) diff --git a/scripts/memory/redismem.py b/scripts/memory/redismem.py new file mode 100644 index 0000000000..e69de29bb2 From 57412bcf4e85c7edff4f021c34390658ee80eb06 Mon Sep 17 00:00:00 2001 From: blankey1337 <42594751+blankey1337@users.noreply.github.com> Date: Thu, 6 Apr 2023 21:16:05 -0700 Subject: [PATCH 035/130] add kandinsky support - cuda issue --- scripts/image_gen.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 scripts/image_gen.py diff --git a/scripts/image_gen.py b/scripts/image_gen.py new file mode 100644 index 0000000000..cdc4fc4d73 --- /dev/null +++ b/scripts/image_gen.py @@ -0,0 +1,44 @@ +from kandinsky2 import get_kandinsky2 +from config import Config + +cfg = Config() + +def generate_image(prompt): + + model = get_kandinsky2('cuda', task_type='text2img', model_version='2.1', use_flash_attention=False) + images = model.generate_text2img( + "red cat, 4k photo", # prompt + num_steps=100, + batch_size=1, + guidance_scale=4, + h=768, w=768, + sampler='p_sampler', + prior_cf_scale=4, + prior_steps="5" + ) + return images + + # base_url = 'http://export.arxiv.org/api/query?' + # query = f'search_query=all:{search_query}&start=0&max_results={max_results}' + # url = base_url + query + # response = requests.get(url) + + # if response.status_code == 200: + # soup = BeautifulSoup(response.content, 'xml') + # entries = soup.find_all('entry') + + # articles = [] + # for entry in entries: + # title = entry.title.text.strip() + # url = entry.id.text.strip() + # published = entry.published.text.strip() + + # articles.append({ + # 'title': title, + # 'url': url, + # 'published': published + # }) + + # return articles + # else: + # return None From 5a1d9e6d0a1752cf08cf747f9279f8b316f3a8c4 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 00:08:25 -0500 Subject: [PATCH 036/130] Implement redis memory backend. --- README.md | 21 ++++++ requirements.txt | 1 + scripts/commands.py | 6 +- scripts/config.py | 7 +- scripts/main.py | 8 ++- scripts/memory/base.py | 1 + scripts/memory/redismem.py | 135 +++++++++++++++++++++++++++++++++++++ 7 files changed, 175 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a89c5d03b7..921f297eed 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,27 @@ are loaded for the agent at any given time. 2. Choose the `Starter` plan to avoid being charged. 3. Find your API key and region under the default project in the left sidebar. + +## Redis Setup + +Install docker desktop. + +Run: +``` +docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest +``` + +Set the following environment variables: +``` +MEMORY_BACKEND=redis +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= +``` + +Note that this is not intended to be run facing the internet and is not secure, do not expose redis to the internet without a password or at all really. + + ### Setting up environment variables For Windows Users: ``` diff --git a/requirements.txt b/requirements.txt index ce24709858..9cfddad627 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ docker duckduckgo-search google-api-python-client #(https://developers.google.com/custom-search/v1/overview) pinecone-client==2.2.1 +redis \ No newline at end of file diff --git a/scripts/commands.py b/scripts/commands.py index f00875f063..98be77727b 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,6 +1,7 @@ import browse import json from memory.pinecone import PineconeMemory +from memory.redismem import RedisMemory import datetime import agent_manager as agents import speak @@ -52,7 +53,10 @@ def get_command(response): def execute_command(command_name, arguments): - memory = PineconeMemory(cfg=cfg) + if cfg.memory_backend == "pinecone": + memory = PineconeMemory(cfg=cfg) + else: + memory = RedisMemory(cfg=cfg) try: if command_name == "google": diff --git a/scripts/config.py b/scripts/config.py index 1b716a3eba..77498d6c91 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -61,7 +61,12 @@ class Config(metaclass=Singleton): # User agent headers to use when browsing web # Some websites might just completely deny request with an error code if no user agent was found. self.user_agent_header = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"} - + self.redis_host = os.getenv("REDIS_HOST") + self.redis_port = os.getenv("REDIS_PORT") + self.redis_password = os.getenv("REDIS_PASSWORD") + # Note that indexes must be created on db 0 in redis, this is not configureable. + + self.memory_backend = os.getenv("MEMORY_BACKEND", 'pinecone') # Initialize the OpenAI API client openai.api_key = self.openai_api_key diff --git a/scripts/main.py b/scripts/main.py index acb63a39be..eecdd7f80e 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -2,6 +2,7 @@ import json import random import commands as cmd from memory.pinecone import PineconeMemory +from memory.redismem import RedisMemory import data import chat from colorama import Fore, Style @@ -283,8 +284,11 @@ user_input = "Determine which next command to use, and respond using the format # Initialize memory and make sure it is empty. # this is particularly important for indexing and referencing pinecone memory -memory = PineconeMemory(cfg) -memory.clear() +if cfg.memory_backend == "pinecone": + memory = PineconeMemory(cfg) + memory.clear() +else: + memory = RedisMemory(cfg) print('Using memory of type: ' + memory.__class__.__name__) diff --git a/scripts/memory/base.py b/scripts/memory/base.py index 29f5d56be1..72349f6be1 100644 --- a/scripts/memory/base.py +++ b/scripts/memory/base.py @@ -1,3 +1,4 @@ +"""Base class for memory providers.""" import abc from config import AbstractSingleton import openai diff --git a/scripts/memory/redismem.py b/scripts/memory/redismem.py index e69de29bb2..162b9269bd 100644 --- a/scripts/memory/redismem.py +++ b/scripts/memory/redismem.py @@ -0,0 +1,135 @@ +"""Redis memory provider.""" +from typing import Any, List, Optional +import redis +from redis.commands.search.field import VectorField, TextField +from redis.commands.search.query import Query +from redis.commands.search.indexDefinition import IndexDefinition, IndexType +import traceback +import numpy as np + +from memory.base import MemoryProviderSingleton, get_ada_embedding + + +SCHEMA = [ + TextField("data"), + VectorField( + "embedding", + "HNSW", + { + "TYPE": "FLOAT32", + "DIM": 1536, + "DISTANCE_METRIC": "COSINE" + } + ), +] + + +class RedisMemory(MemoryProviderSingleton): + def __init__(self, cfg): + """ + Initializes the Redis memory provider. + + Args: + cfg: The config object. + + Returns: None + """ + redis_host = cfg.redis_host + redis_port = cfg.redis_port + redis_password = cfg.redis_password + self.dimension = 1536 + self.redis = redis.Redis( + host=redis_host, + port=redis_port, + password=redis_password, + db=0 # Cannot be changed + ) + self.redis.flushall() + try: + self.redis.ft("gpt").create_index( + fields=SCHEMA, + definition=IndexDefinition( + prefix=["gpt:"], + index_type=IndexType.HASH + ) + ) + except Exception as e: + print("Error creating Redis search index: ", e) + self.vec_num = 0 + + def add(self, data: str) -> str: + """ + Adds a data point to the memory. + + Args: + data: The data to add. + + Returns: Message indicating that the data has been added. + """ + vector = get_ada_embedding(data) + vector = np.array(vector).astype(np.float32).tobytes() + data_dict = { + b"data": data, + "embedding": vector + } + self.redis.hset(f"gpt:{self.vec_num}", mapping=data_dict) + _text = f"Inserting data into memory at index: {self.vec_num}:\n"\ + f"data: {data}" + self.vec_num += 1 + return _text + + def get(self, data: str) -> Optional[List[Any]]: + """ + Gets the data from the memory that is most relevant to the given data. + + Args: + data: The data to compare to. + + Returns: The most relevant data. + """ + return self.get_relevant(data, 1) + + def clear(self) -> str: + """ + Clears the redis server. + + Returns: A message indicating that the memory has been cleared. + """ + self.redis.flushall() + return "Obliviated" + + def get_relevant( + self, + data: str, + num_relevant: int = 5 + ) -> Optional[List[Any]]: + """ + Returns all the data in the memory that is relevant to the given data. + Args: + data: The data to compare to. + num_relevant: The number of relevant data to return. + + Returns: A list of the most relevant data. + """ + query_embedding = get_ada_embedding(data) + base_query = f"*=>[KNN {num_relevant} @embedding $vector AS vector_score]" + query = Query(base_query).return_fields( + "data", + "vector_score" + ).sort_by("vector_score").dialect(2) + query_vector = np.array(query_embedding).astype(np.float32).tobytes() + + try: + results = self.redis.ft("gpt").search( + query, query_params={"vector": query_vector} + ) + except Exception as e: + print("Error calling Redis search: ", e) + return None + return list(results.docs) + + def get_stats(self): + """ + Returns: The stats of the memory index. + """ + return self.redis.ft("mem").info() From cce79695fa43a9abb5aca8e368f7951924c3ae9c Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 00:48:27 -0500 Subject: [PATCH 037/130] Save redis memory state, with the default being to wipe on start still. --- scripts/config.py | 3 ++- scripts/memory/redismem.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index 77498d6c91..8c582a1572 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -64,8 +64,9 @@ class Config(metaclass=Singleton): self.redis_host = os.getenv("REDIS_HOST") self.redis_port = os.getenv("REDIS_PORT") self.redis_password = os.getenv("REDIS_PASSWORD") + self.wipe_redis_on_start = os.getenv("WIPE_REDIS_ON_START", "True") == 'True' # Note that indexes must be created on db 0 in redis, this is not configureable. - + self.memory_backend = os.getenv("MEMORY_BACKEND", 'pinecone') # Initialize the OpenAI API client openai.api_key = self.openai_api_key diff --git a/scripts/memory/redismem.py b/scripts/memory/redismem.py index 162b9269bd..e7021066fa 100644 --- a/scripts/memory/redismem.py +++ b/scripts/memory/redismem.py @@ -44,7 +44,8 @@ class RedisMemory(MemoryProviderSingleton): password=redis_password, db=0 # Cannot be changed ) - self.redis.flushall() + if cfg.wipe_redis_on_start: + self.redis.flushall() try: self.redis.ft("gpt").create_index( fields=SCHEMA, @@ -55,7 +56,9 @@ class RedisMemory(MemoryProviderSingleton): ) except Exception as e: print("Error creating Redis search index: ", e) - self.vec_num = 0 + existing_vec_num = self.redis.get('vec_num') + self.vec_num = int(existing_vec_num.decode('utf-8')) if\ + existing_vec_num else 0 def add(self, data: str) -> str: """ @@ -72,10 +75,13 @@ class RedisMemory(MemoryProviderSingleton): b"data": data, "embedding": vector } - self.redis.hset(f"gpt:{self.vec_num}", mapping=data_dict) + pipe = self.redis.pipeline() + pipe.hset(f"gpt:{self.vec_num}", mapping=data_dict) _text = f"Inserting data into memory at index: {self.vec_num}:\n"\ f"data: {data}" self.vec_num += 1 + pipe.set('vec_num', self.vec_num) + pipe.execute() return _text def get(self, data: str) -> Optional[List[Any]]: From 43746b1396fe47feae9447a72bbaa15ce2c0960a Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 00:58:57 -0500 Subject: [PATCH 038/130] Update README with WIPE_REDIS_ON_START setting. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 921f297eed..7d83b4633f 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,13 @@ REDIS_PASSWORD= Note that this is not intended to be run facing the internet and is not secure, do not expose redis to the internet without a password or at all really. +You can optionally set + +``` +WIPE_REDIS_ON_START=False +``` + +To persist memory stored in Redis. ### Setting up environment variables For Windows Users: From c1034df2d3edb2d95807f1c1ffe0496e9f1e2046 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Fri, 7 Apr 2023 17:25:22 +0900 Subject: [PATCH 039/130] fix typo in json_parser.py specifed -> specified --- scripts/json_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 8ec9238b4d..f44cf4d335 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -53,7 +53,7 @@ def fix_json(json_str: str, schema: str, debug=False) -> str: # Try to fix the JSON using gpt: function_string = "def fix_json(json_str: str, schema:str=None) -> str:" args = [f"'''{json_str}'''", f"'''{schema}'''"] - description_string = """Fixes the provided JSON string to make it parseable and fully complient with the provided schema.\n If an object or field specifed in the schema isn't contained within the correct JSON, it is ommited.\n This function is brilliant at guessing when the format is incorrect.""" + description_string = """Fixes the provided JSON string to make it parseable and fully complient with the provided schema.\n If an object or field specified in the schema isn't contained within the correct JSON, it is ommited.\n This function is brilliant at guessing when the format is incorrect.""" # If it doesn't already start with a "`", add one: if not json_str.startswith("`"): @@ -75,4 +75,4 @@ def fix_json(json_str: str, schema: str, debug=False) -> str: # import traceback # call_stack = traceback.format_exc() # print(f"Failed to fix JSON: '{json_str}' "+call_stack) - return "failed" \ No newline at end of file + return "failed" From 28cc9865e487dec005ef98cffca059cf81c1c806 Mon Sep 17 00:00:00 2001 From: blankey1337 <42594751+blankey1337@users.noreply.github.com> Date: Fri, 7 Apr 2023 08:02:48 -0700 Subject: [PATCH 040/130] feat(ImageGen): add stable diffusion support --- scripts/commands.py | 3 +++ scripts/config.py | 2 ++ scripts/data/prompt.txt | 1 + scripts/image_gen.py | 54 +++++++++++++++-------------------------- 4 files changed, 25 insertions(+), 35 deletions(-) diff --git a/scripts/commands.py b/scripts/commands.py index fc10d1d052..bf8d79833f 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -9,6 +9,7 @@ import ai_functions as ai from file_operations import read_file, write_to_file, append_to_file, delete_file, search_files from execute_code import execute_python_file from json_parser import fix_and_parse_json +from image_gen import generate_image from duckduckgo_search import ddg from googleapiclient.discovery import build from googleapiclient.errors import HttpError @@ -102,6 +103,8 @@ def execute_command(command_name, arguments): return ai.write_tests(arguments["code"], arguments.get("focus")) elif command_name == "execute_python_file": # Add this command return execute_python_file(arguments["file"]) + elif command_name == "generate_image": # Add this command + return generate_image(arguments["prompt"]) elif command_name == "task_complete": shutdown() else: diff --git a/scripts/config.py b/scripts/config.py index fe48d29800..2eca16751f 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -53,6 +53,8 @@ class Config(metaclass=Singleton): self.pinecone_api_key = os.getenv("PINECONE_API_KEY") self.pinecone_region = os.getenv("PINECONE_ENV") + self.huggingface_api_token = os.getenv("HUGGINGFACE_API_TOKEN") + # User agent headers to use when browsing web # Some websites might just completely deny request with an error code if no user agent was found. self.user_agent_header = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"} diff --git a/scripts/data/prompt.txt b/scripts/data/prompt.txt index 28797d9e24..363342c071 100644 --- a/scripts/data/prompt.txt +++ b/scripts/data/prompt.txt @@ -23,6 +23,7 @@ COMMANDS: 17. Write Tests: "write_tests", args: "code": "", "focus": "" 18. Execute Python File: "execute_python_file", args: "file": "" 19. Task Complete (Shutdown): "task_complete", args: "reason": "" +20. Generate Image: "generate_image", args: "prompt": "" RESOURCES: diff --git a/scripts/image_gen.py b/scripts/image_gen.py index cdc4fc4d73..bb3e7686e9 100644 --- a/scripts/image_gen.py +++ b/scripts/image_gen.py @@ -1,44 +1,28 @@ -from kandinsky2 import get_kandinsky2 +import requests +import io +import os.path +from PIL import Image from config import Config +import uuid cfg = Config() +working_directory = "auto_gpt_workspace" + +API_URL = "https://api-inference.huggingface.co/models/CompVis/stable-diffusion-v1-4" +headers = {"Authorization": "Bearer " + cfg.huggingface_api_token} + def generate_image(prompt): - - model = get_kandinsky2('cuda', task_type='text2img', model_version='2.1', use_flash_attention=False) - images = model.generate_text2img( - "red cat, 4k photo", # prompt - num_steps=100, - batch_size=1, - guidance_scale=4, - h=768, w=768, - sampler='p_sampler', - prior_cf_scale=4, - prior_steps="5" - ) - return images - - # base_url = 'http://export.arxiv.org/api/query?' - # query = f'search_query=all:{search_query}&start=0&max_results={max_results}' - # url = base_url + query - # response = requests.get(url) + response = requests.post(API_URL, headers=headers, json={ + "inputs": prompt, + }) + image = Image.open(io.BytesIO(response.content)) + print("Image Generated for prompt:" + prompt) - # if response.status_code == 200: - # soup = BeautifulSoup(response.content, 'xml') - # entries = soup.find_all('entry') + filename = str(uuid.uuid4()) + ".jpg" - # articles = [] - # for entry in entries: - # title = entry.title.text.strip() - # url = entry.id.text.strip() - # published = entry.published.text.strip() + image.save(os.path.join(working_directory, filename)) - # articles.append({ - # 'title': title, - # 'url': url, - # 'published': published - # }) + print("Saved to disk:" + filename) - # return articles - # else: - # return None + return str("Image " + filename + " saved to disk for prompt: " + prompt) From ae97790cecc8a2aeafe65e6d19d17c4aae829c73 Mon Sep 17 00:00:00 2001 From: Monkee1337 <118180627+Monkee1337@users.noreply.github.com> Date: Fri, 7 Apr 2023 18:17:20 +0200 Subject: [PATCH 041/130] Update README.md Co-authored-by: Arlo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6d06f34e5..e3fb981394 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ are loaded for the agent at any given time. Simply set them in the `.env` file. -Alternatively you can use the appropriate following method (advanced): +Alternatively, you can set them from the command line (advanced): For Windows Users: ``` From b56b04e86f4bcd93297cbe48efb8d9117be2566e Mon Sep 17 00:00:00 2001 From: blankey1337 <42594751+blankey1337@users.noreply.github.com> Date: Fri, 7 Apr 2023 11:03:23 -0700 Subject: [PATCH 042/130] feat(ImageGen): add DALL-E support --- scripts/image_gen.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/scripts/image_gen.py b/scripts/image_gen.py index bb3e7686e9..92cda29082 100644 --- a/scripts/image_gen.py +++ b/scripts/image_gen.py @@ -4,6 +4,8 @@ import os.path from PIL import Image from config import Config import uuid +import openai +from base64 import b64decode cfg = Config() @@ -13,16 +15,36 @@ API_URL = "https://api-inference.huggingface.co/models/CompVis/stable-diffusion- headers = {"Authorization": "Bearer " + cfg.huggingface_api_token} def generate_image(prompt): + + filename = str(uuid.uuid4()) + ".jpg" + + # DALL-E + openai.api_key = cfg.openai_api_key + + response = openai.Image.create( + prompt=prompt, + n=1, + size="256x256", + response_format="b64_json", + ) + + print("Image Generated for prompt:" + prompt) + print(response["data"][0]["b64_json"][:50]) + + image_data = b64decode(response["data"][0]["b64_json"]) + with open(working_directory + "/" + filename, mode="wb") as png: + png.write(image_data) + + return "Saved to disk:" + filename + + # STABLE DIFFUSION response = requests.post(API_URL, headers=headers, json={ "inputs": prompt, }) image = Image.open(io.BytesIO(response.content)) print("Image Generated for prompt:" + prompt) - filename = str(uuid.uuid4()) + ".jpg" - image.save(os.path.join(working_directory, filename)) - print("Saved to disk:" + filename) return str("Image " + filename + " saved to disk for prompt: " + prompt) From f3e64ec4e9128d4757bf5ffadbb73a9b144b2ecb Mon Sep 17 00:00:00 2001 From: blankey1337 <42594751+blankey1337@users.noreply.github.com> Date: Fri, 7 Apr 2023 11:29:43 -0700 Subject: [PATCH 043/130] feat(ImageGen): support env vars, update readme --- .env.template | 4 ++- README.md | 11 +++++++++ scripts/image_gen.py | 59 +++++++++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/.env.template b/.env.template index e9ccda5edb..525cd61c5f 100644 --- a/.env.template +++ b/.env.template @@ -9,4 +9,6 @@ CUSTOM_SEARCH_ENGINE_ID= USE_AZURE=False OPENAI_API_BASE=your-base-url-for-azure OPENAI_API_VERSION=api-version-for-azure -OPENAI_DEPLOYMENT_ID=deployment-id-for-azure \ No newline at end of file +OPENAI_DEPLOYMENT_ID=deployment-id-for-azure +IMAGE_PROVIDER=dalle +HUGGINGFACE_API_TOKEN= \ No newline at end of file diff --git a/README.md b/README.md index a89c5d03b7..f6cf6093e5 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Your support is greatly appreciated - [Setting up environment variables](#setting-up-environment-variables) - [πŸ’€ Continuous Mode ⚠️](#-continuous-mode-️) - [GPT3.5 ONLY Mode](#gpt35-only-mode) + - [πŸ–Ό Image Generation](#image-generation) - [⚠️ Limitations](#️-limitations) - [πŸ›‘ Disclaimer](#-disclaimer) - [🐦 Connect with Us on Twitter](#-connect-with-us-on-twitter) @@ -169,6 +170,7 @@ Or you can set them in the `.env` file. 1. View memory usage by using the `--debug` flag :) + ## πŸ’€ Continuous Mode ⚠️ Run the AI **without** user authorisation, 100% automated. Continuous mode is not recommended. @@ -187,6 +189,15 @@ If you don't have access to the GPT4 api, this mode will allow you to use Auto-G python scripts/main.py --gpt3only ``` +## πŸ–Ό Image Generation +By default, Auto-GPT uses DALL-e for image generation. To use Stable Diffusion, a [HuggingFace API Token](https://huggingface.co/settings/tokens) is required. + +Once you have a token, set these variables in your `.env`: +``` +IMAGE_PROVIDER=sd +HUGGINGFACE_API_TOKEN="YOUR_HUGGINGFACE_API_TOKEN" +``` + ## ⚠️ Limitations This experiment aims to showcase the potential of GPT-4 but comes with some limitations: diff --git a/scripts/image_gen.py b/scripts/image_gen.py index 92cda29082..deda7ed5af 100644 --- a/scripts/image_gen.py +++ b/scripts/image_gen.py @@ -11,40 +11,49 @@ cfg = Config() working_directory = "auto_gpt_workspace" -API_URL = "https://api-inference.huggingface.co/models/CompVis/stable-diffusion-v1-4" -headers = {"Authorization": "Bearer " + cfg.huggingface_api_token} - def generate_image(prompt): filename = str(uuid.uuid4()) + ".jpg" - + # DALL-E - openai.api_key = cfg.openai_api_key + if cfg.image_provider == 'dalle': + + openai.api_key = cfg.openai_api_key - response = openai.Image.create( - prompt=prompt, - n=1, - size="256x256", - response_format="b64_json", - ) + response = openai.Image.create( + prompt=prompt, + n=1, + size="256x256", + response_format="b64_json", + ) - print("Image Generated for prompt:" + prompt) - print(response["data"][0]["b64_json"][:50]) + print("Image Generated for prompt:" + prompt) + print(response["data"][0]["b64_json"][:50]) - image_data = b64decode(response["data"][0]["b64_json"]) - with open(working_directory + "/" + filename, mode="wb") as png: - png.write(image_data) + image_data = b64decode(response["data"][0]["b64_json"]) - return "Saved to disk:" + filename + with open(working_directory + "/" + filename, mode="wb") as png: + png.write(image_data) + + return "Saved to disk:" + filename # STABLE DIFFUSION - response = requests.post(API_URL, headers=headers, json={ - "inputs": prompt, - }) - image = Image.open(io.BytesIO(response.content)) - print("Image Generated for prompt:" + prompt) + elif cfg.image_provider == 'sd': - image.save(os.path.join(working_directory, filename)) - print("Saved to disk:" + filename) + API_URL = "https://api-inference.huggingface.co/models/CompVis/stable-diffusion-v1-4" + headers = {"Authorization": "Bearer " + cfg.huggingface_api_token} - return str("Image " + filename + " saved to disk for prompt: " + prompt) + response = requests.post(API_URL, headers=headers, json={ + "inputs": prompt, + }) + + image = Image.open(io.BytesIO(response.content)) + print("Image Generated for prompt:" + prompt) + + image.save(os.path.join(working_directory, filename)) + print("Saved to disk:" + filename) + + return str("Image " + filename + " saved to disk for prompt: " + prompt) + + else: + return "No Image Provider Set" \ No newline at end of file From 091db1d4c3db6a3bf4bc50e882f299719bc65c60 Mon Sep 17 00:00:00 2001 From: blankey1337 <42594751+blankey1337@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:47:49 -0700 Subject: [PATCH 044/130] chore(ImageGen): cleanup --- scripts/config.py | 1 + scripts/image_gen.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index 2eca16751f..959c3eb226 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -53,6 +53,7 @@ class Config(metaclass=Singleton): self.pinecone_api_key = os.getenv("PINECONE_API_KEY") self.pinecone_region = os.getenv("PINECONE_ENV") + self.image_provider = os.getenv("IMAGE_PROVIDER") self.huggingface_api_token = os.getenv("HUGGINGFACE_API_TOKEN") # User agent headers to use when browsing web diff --git a/scripts/image_gen.py b/scripts/image_gen.py index deda7ed5af..185ed4278b 100644 --- a/scripts/image_gen.py +++ b/scripts/image_gen.py @@ -17,7 +17,7 @@ def generate_image(prompt): # DALL-E if cfg.image_provider == 'dalle': - + openai.api_key = cfg.openai_api_key response = openai.Image.create( @@ -28,7 +28,6 @@ def generate_image(prompt): ) print("Image Generated for prompt:" + prompt) - print(response["data"][0]["b64_json"][:50]) image_data = b64decode(response["data"][0]["b64_json"]) @@ -51,9 +50,8 @@ def generate_image(prompt): print("Image Generated for prompt:" + prompt) image.save(os.path.join(working_directory, filename)) - print("Saved to disk:" + filename) - return str("Image " + filename + " saved to disk for prompt: " + prompt) + return "Saved to disk:" + filename else: return "No Image Provider Set" \ No newline at end of file From f0162037c341e31583d09626da2c853563cc4776 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:02:22 -0500 Subject: [PATCH 045/130] Fix README --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7d83b4633f..b7f514e830 100644 --- a/README.md +++ b/README.md @@ -140,16 +140,6 @@ export CUSTOM_SEARCH_ENGINE_ID="YOUR_CUSTOM_SEARCH_ENGINE_ID" ``` -## 🌲 Pinecone API Key Setup - -Pinecone enable a vector based memory so a vast memory can be stored and only relevant memories -are loaded for the agent at any given time. - -1. Go to app.pinecone.io and make an account if you don't already have one. -2. Choose the `Starter` plan to avoid being charged. -3. Find your API key and region under the default project in the left sidebar. - - ## Redis Setup Install docker desktop. @@ -177,6 +167,15 @@ WIPE_REDIS_ON_START=False To persist memory stored in Redis. +## 🌲 Pinecone API Key Setup + +Pinecone enable a vector based memory so a vast memory can be stored and only relevant memories +are loaded for the agent at any given time. + +1. Go to app.pinecone.io and make an account if you don't already have one. +2. Choose the `Starter` plan to avoid being charged. +3. Find your API key and region under the default project in the left sidebar. + ### Setting up environment variables For Windows Users: ``` From 5d13fb2546916f2b5ff360720b07706ab31e6e21 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:03:20 -0500 Subject: [PATCH 046/130] Remove unused function. --- scripts/memory/base.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/memory/base.py b/scripts/memory/base.py index 72349f6be1..d7ab7fcf1f 100644 --- a/scripts/memory/base.py +++ b/scripts/memory/base.py @@ -9,10 +9,6 @@ def get_ada_embedding(text): return openai.Embedding.create(input=[text], model="text-embedding-ada-002")["data"][0]["embedding"] -def get_text_from_embedding(embedding): - return openai.Embedding.retrieve(embedding, model="text-embedding-ada-002")["data"][0]["text"] - - class MemoryProviderSingleton(AbstractSingleton): @abc.abstractmethod def add(self, data): From 14e10c9c4ddc1d0736b4161e96d0c2517c65b12a Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:27:48 -0500 Subject: [PATCH 047/130] Add configurable index key for redis. --- scripts/config.py | 1 + scripts/memory/redismem.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index 8c582a1572..637c17fdf8 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -65,6 +65,7 @@ class Config(metaclass=Singleton): self.redis_port = os.getenv("REDIS_PORT") self.redis_password = os.getenv("REDIS_PASSWORD") self.wipe_redis_on_start = os.getenv("WIPE_REDIS_ON_START", "True") == 'True' + self.memory_index = os.getenv("MEMORY_INDEX", 'gpt') # Note that indexes must be created on db 0 in redis, this is not configureable. self.memory_backend = os.getenv("MEMORY_BACKEND", 'pinecone') diff --git a/scripts/memory/redismem.py b/scripts/memory/redismem.py index e7021066fa..20be4a4e84 100644 --- a/scripts/memory/redismem.py +++ b/scripts/memory/redismem.py @@ -44,19 +44,20 @@ class RedisMemory(MemoryProviderSingleton): password=redis_password, db=0 # Cannot be changed ) + self.cfg = cfg if cfg.wipe_redis_on_start: self.redis.flushall() try: - self.redis.ft("gpt").create_index( + self.redis.ft(f"{cfg.memory_index}").create_index( fields=SCHEMA, definition=IndexDefinition( - prefix=["gpt:"], + prefix=[f"{cfg.memory_index}:"], index_type=IndexType.HASH ) ) except Exception as e: print("Error creating Redis search index: ", e) - existing_vec_num = self.redis.get('vec_num') + existing_vec_num = self.redis.get(f'{cfg.memory_index}-vec_num') self.vec_num = int(existing_vec_num.decode('utf-8')) if\ existing_vec_num else 0 @@ -76,11 +77,11 @@ class RedisMemory(MemoryProviderSingleton): "embedding": vector } pipe = self.redis.pipeline() - pipe.hset(f"gpt:{self.vec_num}", mapping=data_dict) + pipe.hset(f"{self.cfg.memory_index}:{self.vec_num}", mapping=data_dict) _text = f"Inserting data into memory at index: {self.vec_num}:\n"\ f"data: {data}" self.vec_num += 1 - pipe.set('vec_num', self.vec_num) + pipe.set(f'{self.cfg.memory_index}-vec_num', self.vec_num) pipe.execute() return _text @@ -126,7 +127,7 @@ class RedisMemory(MemoryProviderSingleton): query_vector = np.array(query_embedding).astype(np.float32).tobytes() try: - results = self.redis.ft("gpt").search( + results = self.redis.ft(f"{self.cfg.memory_index}").search( query, query_params={"vector": query_vector} ) except Exception as e: @@ -138,4 +139,4 @@ class RedisMemory(MemoryProviderSingleton): """ Returns: The stats of the memory index. """ - return self.redis.ft("mem").info() + return self.redis.ft(f"{self.cfg.memory_index}").info() From ea6b97050948487cee5ee50a12f7eb2a161e0648 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:28:48 -0500 Subject: [PATCH 048/130] Update README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b7f514e830..5ce001b094 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,12 @@ WIPE_REDIS_ON_START=False To persist memory stored in Redis. +You can specify the memory index for redis using the following: + +```` +MEMORY_INDEX=whatever +```` + ## 🌲 Pinecone API Key Setup Pinecone enable a vector based memory so a vast memory can be stored and only relevant memories From 7f4a7d7ccdbe2033a8c90cd72f416dec10724a58 Mon Sep 17 00:00:00 2001 From: Coley Date: Fri, 7 Apr 2023 16:47:09 -0400 Subject: [PATCH 049/130] Added class, method and function doc strings --- scripts/ai_config.py | 60 +++++++++++++++++++++++++++++++++++++---- scripts/ai_functions.py | 32 +++++++++++++++++----- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 2f43274863..6ac243c295 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -3,7 +3,25 @@ import data class AIConfig: - def __init__(self, ai_name="", ai_role="", ai_goals=[]): + """ + A class object that contains the configuration information for the AI + + Attributes: + ai_name (str): The name of the AI. + ai_role (str): The description of the AI's role. + ai_goals (list): The list of objectives the AI is supposed to complete. + """ + def __init__(self, ai_name:str="", ai_role:str="", ai_goals:list=[]) -> None: + """ + Initialize a class instance + + Parameters: + ai_name (str): The name of the AI. + ai_role (str): The description of the AI's role. + ai_goals (list): The list of objectives the AI is supposed to complete. + Returns: + None + """ self.ai_name = ai_name self.ai_role = ai_role self.ai_goals = ai_goals @@ -12,8 +30,18 @@ class AIConfig: SAVE_FILE = "../ai_settings.yaml" @classmethod - def load(cls, config_file=SAVE_FILE): - # Load variables from yaml file if it exists + def load(cls:object, config_file:str=SAVE_FILE) -> object: + """ + Returns class object with parameters (ai_name, ai_role, ai_goals) loaded from yaml file if yaml file exists, + else returns class with no parameters. + + Parameters: + cls (class object): An AIConfig Class object. + config_file (int): The path to the config yaml file. DEFAULT: "../ai_settings.yaml" + + Returns: + cls (object): A instance of given cls object + """ try: with open(config_file) as file: config_params = yaml.load(file, Loader=yaml.FullLoader) @@ -26,12 +54,30 @@ class AIConfig: return cls(ai_name, ai_role, ai_goals) - def save(self, config_file=SAVE_FILE): + def save(self, config_file:str=SAVE_FILE) -> None: + """ + Saves the class parameters to the specified file yaml file path as a yaml file. + + Parameters: + config_file(str): The path to the config yaml file. DEFAULT: "../ai_settings.yaml" + + Returns: + None + """ config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals} with open(config_file, "w") as file: yaml.dump(config, file) - def construct_full_prompt(self): + def construct_full_prompt(self) -> str: + """ + Returns a prompt to the user with the class information in an organized fashion. + + Parameters: + None + + Returns: + full_prompt (str): A string containing the intitial prompt for the user including the ai_name, ai_role and ai_goals. + """ prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.""" # Construct full prompt @@ -41,3 +87,7 @@ class AIConfig: full_prompt += f"\n\n{data.load_prompt()}" return full_prompt + +if __name__ == "__main__": + import doctest + doctest.testmod() \ No newline at end of file diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index 05aa93a2da..e54f096698 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -5,9 +5,16 @@ from call_ai_function import call_ai_function from json_parser import fix_and_parse_json cfg = Config() -# Evaluating code def evaluate_code(code: str) -> List[str]: + """ + A function that takes in a string and returns a response from create chat completion api call. + + Parameters: + code (str): Code to be evaluated. + Returns: + A result string from create chat completion. A list of suggestions to improve the code. + """ function_string = "def analyze_code(code: str) -> List[str]:" args = [code] description_string = """Analyzes the given code and returns a list of suggestions for improvements.""" @@ -17,9 +24,16 @@ def evaluate_code(code: str) -> List[str]: return result_string -# Improving code - def improve_code(suggestions: List[str], code: str) -> str: + """ + A function that takes in code and suggestions and returns a response from create chat completion api call. + + Parameters: + suggestions (List): A list of suggestions around what needs to be improved. + code (str): Code to be improved. + Returns: + A result string from create chat completion. Improved code in response. + """ function_string = ( "def generate_improved_code(suggestions: List[str], code: str) -> str:" ) @@ -30,10 +44,16 @@ def improve_code(suggestions: List[str], code: str) -> str: return result_string -# Writing tests - - def write_tests(code: str, focus: List[str]) -> str: + """ + A function that takes in code and focus topics and returns a response from create chat completion api call. + + Parameters: + focus (List): A list of suggestions around what needs to be improved. + code (str): Code for test cases to be generated against. + Returns: + A result string from create chat completion. Test cases for the submitted code in response. + """ function_string = ( "def create_test_cases(code: str, focus: Optional[str] = None) -> str:" ) From 7ae5ffe389729a2f5e4c1d859e5fae1b687ba94a Mon Sep 17 00:00:00 2001 From: Coley Date: Fri, 7 Apr 2023 16:52:50 -0400 Subject: [PATCH 050/130] linted changes --- scripts/ai_config.py | 24 +++++++++++++----------- scripts/ai_functions.py | 10 ++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 6ac243c295..4656f23a07 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -5,16 +5,17 @@ import data class AIConfig: """ A class object that contains the configuration information for the AI - + Attributes: ai_name (str): The name of the AI. ai_role (str): The description of the AI's role. ai_goals (list): The list of objectives the AI is supposed to complete. """ - def __init__(self, ai_name:str="", ai_role:str="", ai_goals:list=[]) -> None: + + def __init__(self, ai_name: str="", ai_role: str="", ai_goals: list=[]) -> None: """ Initialize a class instance - + Parameters: ai_name (str): The name of the AI. ai_role (str): The description of the AI's role. @@ -30,15 +31,15 @@ class AIConfig: SAVE_FILE = "../ai_settings.yaml" @classmethod - def load(cls:object, config_file:str=SAVE_FILE) -> object: + def load(cls: object, config_file: str=SAVE_FILE) -> object: """ Returns class object with parameters (ai_name, ai_role, ai_goals) loaded from yaml file if yaml file exists, else returns class with no parameters. - + Parameters: cls (class object): An AIConfig Class object. config_file (int): The path to the config yaml file. DEFAULT: "../ai_settings.yaml" - + Returns: cls (object): A instance of given cls object """ @@ -54,13 +55,13 @@ class AIConfig: return cls(ai_name, ai_role, ai_goals) - def save(self, config_file:str=SAVE_FILE) -> None: + def save(self, config_file: str=SAVE_FILE) -> None: """ Saves the class parameters to the specified file yaml file path as a yaml file. - + Parameters: config_file(str): The path to the config yaml file. DEFAULT: "../ai_settings.yaml" - + Returns: None """ @@ -71,10 +72,10 @@ class AIConfig: def construct_full_prompt(self) -> str: """ Returns a prompt to the user with the class information in an organized fashion. - + Parameters: None - + Returns: full_prompt (str): A string containing the intitial prompt for the user including the ai_name, ai_role and ai_goals. """ @@ -88,6 +89,7 @@ class AIConfig: full_prompt += f"\n\n{data.load_prompt()}" return full_prompt + if __name__ == "__main__": import doctest doctest.testmod() \ No newline at end of file diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index e54f096698..eee26e1529 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -9,7 +9,7 @@ cfg = Config() def evaluate_code(code: str) -> List[str]: """ A function that takes in a string and returns a response from create chat completion api call. - + Parameters: code (str): Code to be evaluated. Returns: @@ -20,14 +20,14 @@ def evaluate_code(code: str) -> List[str]: description_string = """Analyzes the given code and returns a list of suggestions for improvements.""" result_string = call_ai_function(function_string, args, description_string) - + return result_string def improve_code(suggestions: List[str], code: str) -> str: """ A function that takes in code and suggestions and returns a response from create chat completion api call. - + Parameters: suggestions (List): A list of suggestions around what needs to be improved. code (str): Code to be improved. @@ -47,7 +47,7 @@ def improve_code(suggestions: List[str], code: str) -> str: def write_tests(code: str, focus: List[str]) -> str: """ A function that takes in code and focus topics and returns a response from create chat completion api call. - + Parameters: focus (List): A list of suggestions around what needs to be improved. code (str): Code for test cases to be generated against. @@ -62,5 +62,3 @@ def write_tests(code: str, focus: List[str]) -> str: result_string = call_ai_function(function_string, args, description_string) return result_string - - From 77c98a03119c1b62777bec50100012298a0967c0 Mon Sep 17 00:00:00 2001 From: Coley Date: Fri, 7 Apr 2023 16:57:39 -0400 Subject: [PATCH 051/130] Removed doctest call --- scripts/ai_config.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 4656f23a07..038d5e5dd2 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -89,7 +89,3 @@ class AIConfig: full_prompt += f"\n\n{data.load_prompt()}" return full_prompt - -if __name__ == "__main__": - import doctest - doctest.testmod() \ No newline at end of file From cb14c8d999c32c89215be04d27fe132a149eb047 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 18:13:18 -0500 Subject: [PATCH 052/130] Implement local memory. --- requirements.txt | 3 +- scripts/commands.py | 8 ++- scripts/config.py | 4 +- scripts/main.py | 5 +- scripts/memory/local.py | 111 +++++++++++++++++++++++++++++++++++++ scripts/memory/redismem.py | 1 - 6 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 scripts/memory/local.py diff --git a/requirements.txt b/requirements.txt index 9cfddad627..5bcc74957e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,5 @@ docker duckduckgo-search google-api-python-client #(https://developers.google.com/custom-search/v1/overview) pinecone-client==2.2.1 -redis \ No newline at end of file +redis +orjson \ No newline at end of file diff --git a/scripts/commands.py b/scripts/commands.py index 98be77727b..a88ad0ae09 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,5 +1,6 @@ import browse import json +from memory.local import LocalCache from memory.pinecone import PineconeMemory from memory.redismem import RedisMemory import datetime @@ -55,11 +56,14 @@ def get_command(response): def execute_command(command_name, arguments): if cfg.memory_backend == "pinecone": memory = PineconeMemory(cfg=cfg) - else: + elif cfg.memory_backend == "redis": memory = RedisMemory(cfg=cfg) + else: + memory = LocalCache(cfg=cfg) + try: if command_name == "google": - + # Check if the Google API key is set and use the official search method # If the API key is not set or has only whitespaces, use the unofficial search method if cfg.google_api_key and (cfg.google_api_key.strip() if cfg.google_api_key else None): diff --git a/scripts/config.py b/scripts/config.py index 637c17fdf8..9afeb1d257 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -65,10 +65,10 @@ class Config(metaclass=Singleton): self.redis_port = os.getenv("REDIS_PORT") self.redis_password = os.getenv("REDIS_PASSWORD") self.wipe_redis_on_start = os.getenv("WIPE_REDIS_ON_START", "True") == 'True' - self.memory_index = os.getenv("MEMORY_INDEX", 'gpt') + self.memory_index = os.getenv("MEMORY_INDEX", 'auto-gpt') # Note that indexes must be created on db 0 in redis, this is not configureable. - self.memory_backend = os.getenv("MEMORY_BACKEND", 'pinecone') + self.memory_backend = os.getenv("MEMORY_BACKEND", 'local') # Initialize the OpenAI API client openai.api_key = self.openai_api_key diff --git a/scripts/main.py b/scripts/main.py index eecdd7f80e..e49f1810d2 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -1,6 +1,7 @@ import json import random import commands as cmd +from memory.local import LocalCache from memory.pinecone import PineconeMemory from memory.redismem import RedisMemory import data @@ -287,8 +288,10 @@ user_input = "Determine which next command to use, and respond using the format if cfg.memory_backend == "pinecone": memory = PineconeMemory(cfg) memory.clear() -else: +elif cfg.memory_backend == "redis": memory = RedisMemory(cfg) +else: + memory = LocalCache(cfg) print('Using memory of type: ' + memory.__class__.__name__) diff --git a/scripts/memory/local.py b/scripts/memory/local.py new file mode 100644 index 0000000000..fb10522426 --- /dev/null +++ b/scripts/memory/local.py @@ -0,0 +1,111 @@ +import dataclasses +import orjson +from typing import Any, List, Optional +import numpy as np +import os +from memory.base import MemoryProviderSingleton, get_ada_embedding + + +EMBED_DIM = 1536 +SAVE_OPTIONS = orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_SERIALIZE_DATACLASS + + +def create_default_embeddings(): + return np.zeros((0, EMBED_DIM)).astype(np.float32) + + +@dataclasses.dataclass +class CacheContent: + texts: List[str] = dataclasses.field(default_factory=list) + embeddings: np.ndarray = dataclasses.field( + default_factory=create_default_embeddings + ) + + +class LocalCache(MemoryProviderSingleton): + + # on load, load our database + def __init__(self, cfg) -> None: + self.filename = f"{cfg.memory_index}.json" + if os.path.exists(self.filename): + with open(self.filename, 'rb') as f: + loaded = orjson.loads(f.read()) + self.data = CacheContent(**loaded) + else: + self.data = CacheContent() + + def add(self, text: str): + """ + Add text to our list of texts, add embedding as row to our + embeddings-matrix + + Args: + text: str + + Returns: None + """ + self.data.texts.append(text) + + embedding = get_ada_embedding(text) + + vector = np.array(embedding).astype(np.float32) + vector = vector[np.newaxis, :] + self.data.embeddings = np.concatenate( + [ + vector, + self.data.embeddings, + ], + axis=0, + ) + + with open(self.filename, 'wb') as f: + out = orjson.dumps( + self.data, + option=SAVE_OPTIONS + ) + f.write(out) + + def clear(self) -> str: + """ + Clears the redis server. + + Returns: A message indicating that the memory has been cleared. + """ + self.data = CacheContent() + return "Obliviated" + + def get(self, data: str) -> Optional[List[Any]]: + """ + Gets the data from the memory that is most relevant to the given data. + + Args: + data: The data to compare to. + + Returns: The most relevant data. + """ + return self.get_relevant(data, 1) + + def get_relevant(self, text: str, k: int) -> List[Any]: + """" + matrix-vector mult to find score-for-each-row-of-matrix + get indices for top-k winning scores + return texts for those indices + Args: + text: str + k: int + + Returns: List[str] + """ + embedding = get_ada_embedding(text) + + scores = np.dot(self.data.embeddings, embedding) + + top_k_indices = np.argsort(scores)[-k:][::-1] + + return [self.data.texts[i] for i in top_k_indices] + + def get_stats(self): + """ + Returns: The stats of the local cache. + """ + return len(self.data.texts), self.data.embeddings.shape diff --git a/scripts/memory/redismem.py b/scripts/memory/redismem.py index 20be4a4e84..296d0cce2c 100644 --- a/scripts/memory/redismem.py +++ b/scripts/memory/redismem.py @@ -4,7 +4,6 @@ import redis from redis.commands.search.field import VectorField, TextField from redis.commands.search.query import Query from redis.commands.search.indexDefinition import IndexDefinition, IndexType -import traceback import numpy as np from memory.base import MemoryProviderSingleton, get_ada_embedding From 503b58b7948fe3a37622919864015607352e76e6 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 18:30:04 -0500 Subject: [PATCH 053/130] Refactor memory into factory. --- scripts/commands.py | 11 ++-------- scripts/main.py | 12 ++--------- scripts/memory/__init__.py | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/scripts/commands.py b/scripts/commands.py index a88ad0ae09..783e6bd295 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,8 +1,6 @@ import browse import json -from memory.local import LocalCache -from memory.pinecone import PineconeMemory -from memory.redismem import RedisMemory +from memory import get_memory import datetime import agent_manager as agents import speak @@ -54,12 +52,7 @@ def get_command(response): def execute_command(command_name, arguments): - if cfg.memory_backend == "pinecone": - memory = PineconeMemory(cfg=cfg) - elif cfg.memory_backend == "redis": - memory = RedisMemory(cfg=cfg) - else: - memory = LocalCache(cfg=cfg) + memory = get_memory(cfg) try: if command_name == "google": diff --git a/scripts/main.py b/scripts/main.py index e49f1810d2..11bf0dc1b2 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -1,9 +1,7 @@ import json import random import commands as cmd -from memory.local import LocalCache -from memory.pinecone import PineconeMemory -from memory.redismem import RedisMemory +from memory import get_memory import data import chat from colorama import Fore, Style @@ -285,13 +283,7 @@ user_input = "Determine which next command to use, and respond using the format # Initialize memory and make sure it is empty. # this is particularly important for indexing and referencing pinecone memory -if cfg.memory_backend == "pinecone": - memory = PineconeMemory(cfg) - memory.clear() -elif cfg.memory_backend == "redis": - memory = RedisMemory(cfg) -else: - memory = LocalCache(cfg) +memory = get_memory(cfg, init=True) print('Using memory of type: ' + memory.__class__.__name__) diff --git a/scripts/memory/__init__.py b/scripts/memory/__init__.py index e69de29bb2..dacb05b328 100644 --- a/scripts/memory/__init__.py +++ b/scripts/memory/__init__.py @@ -0,0 +1,42 @@ +from memory.local import LocalCache +try: + from memory.redismem import RedisMemory +except ImportError: + print("Redis not installed. Skipping import.") + RedisMemory = None + +try: + from memory.pinecone import PineconeMemory +except ImportError: + print("Pinecone not installed. Skipping import.") + PineconeMemory = None + + +def get_memory(cfg, init=False): + memory = None + if cfg.memory_backend == "pinecone": + if not PineconeMemory: + print("Error: Pinecone is not installed. Please install pinecone" + " to use Pinecone as a memory backend.") + else: + memory = PineconeMemory(cfg) + if init: + memory.clear() + elif cfg.memory_backend == "redis": + if not RedisMemory: + print("Error: Redis is not installed. Please install redis-py to" + " use Redis as a memory backend.") + else: + memory = RedisMemory(cfg) + + if memory is None: + memory = LocalCache(cfg) + return memory + + +__all__ = [ + "get_memory", + "LocalCache", + "RedisCache", + "PineconeCache", +] From b983faa95339843f170258bd06923f6b81c1dc42 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Sat, 8 Apr 2023 04:36:10 +0500 Subject: [PATCH 054/130] [fix]: Added an exception in main.py if pinecone_api_key is not provided --- scripts/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index a79fd553ce..17385bf339 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -281,11 +281,12 @@ next_action_count = 0 # Make a constant: user_input = "Determine which next command to use, and respond using the format specified above:" +# raise an exception if pinecone_api_key or region is not provided +if not cfg.pinecone_api_key or not cfg.pinecone_region: raise Exception("Please provide pinecone_api_key and pinecone_region") # Initialize memory and make sure it is empty. # this is particularly important for indexing and referencing pinecone memory memory = PineconeMemory() memory.clear() - print('Using memory of type: ' + memory.__class__.__name__) # Interaction Loop From 1d0848c1a89e6c29e5fde1688bcdfa267de07974 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Sat, 8 Apr 2023 04:37:46 +0500 Subject: [PATCH 055/130] [docs]: Updated ReadMe to update the requirements sections --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a89c5d03b7..4fc0c349e5 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ Your support is greatly appreciated ## πŸ“‹ Requirements - [Python 3.7 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows) - OpenAI API key +- PINECONE API key Optional: - ElevenLabs Key (If you want the AI to speak) From a34c51bf8622cf83a34493718c8be60c0676e603 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Fri, 7 Apr 2023 20:58:00 -0500 Subject: [PATCH 056/130] Update scripts/config.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jason KΓΆlker --- scripts/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index 9afeb1d257..1601dcc43f 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -61,9 +61,9 @@ class Config(metaclass=Singleton): # User agent headers to use when browsing web # Some websites might just completely deny request with an error code if no user agent was found. self.user_agent_header = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"} - self.redis_host = os.getenv("REDIS_HOST") - self.redis_port = os.getenv("REDIS_PORT") - self.redis_password = os.getenv("REDIS_PASSWORD") + self.redis_host = os.getenv("REDIS_HOST", "localhost") + self.redis_port = os.getenv("REDIS_PORT", "6379") + self.redis_password = os.getenv("REDIS_PASSWORD", "") self.wipe_redis_on_start = os.getenv("WIPE_REDIS_ON_START", "True") == 'True' self.memory_index = os.getenv("MEMORY_INDEX", 'auto-gpt') # Note that indexes must be created on db 0 in redis, this is not configureable. From 9328c8f7b5b9b7eb76dd131f36ad6109e8b28e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Machado=20de=20Oliveira?= Date: Sat, 8 Apr 2023 00:15:14 -0300 Subject: [PATCH 057/130] Settings were being saved and loaded in the wrong directory --- scripts/ai_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 2f43274863..8cfa183a9b 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -1,6 +1,6 @@ import yaml import data - +import os class AIConfig: def __init__(self, ai_name="", ai_role="", ai_goals=[]): @@ -9,7 +9,7 @@ class AIConfig: self.ai_goals = ai_goals # Soon this will go in a folder where it remembers more stuff about the run(s) - SAVE_FILE = "../ai_settings.yaml" + SAVE_FILE = os.path.join(os.path.dirname(__file__), '..', 'ai_settings.yaml') @classmethod def load(cls, config_file=SAVE_FILE): From 91fe21e64dbab9ff314b513f3b5787fbd49831f4 Mon Sep 17 00:00:00 2001 From: kinance Date: Sat, 8 Apr 2023 12:39:57 +0900 Subject: [PATCH 058/130] Revised to support debug mode from command line --- .gitignore | 3 ++- scripts/chat.py | 6 +++--- scripts/config.py | 4 ++++ scripts/json_parser.py | 2 +- scripts/main.py | 4 ++++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 7091a87237..956242a45b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ auto_gpt_workspace/* *.mpeg .env outputs/* -ai_settings.yaml \ No newline at end of file +ai_settings.yaml +.venv/* \ No newline at end of file diff --git a/scripts/chat.py b/scripts/chat.py index 8da074c6bf..6ad5f47898 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -62,13 +62,13 @@ def chat_with_ai( """ model = cfg.fast_llm_model # TODO: Change model from hardcode to argument # Reserve 1000 tokens for the response - if debug: + if cfg.debug_mode: print(f"Token limit: {token_limit}") send_token_limit = token_limit - 1000 relevant_memory = permanent_memory.get_relevant(str(full_message_history[-5:]), 10) - if debug: + if cfg.debug_mode: print('Memory Stats: ', permanent_memory.get_stats()) next_message_to_add_index, current_tokens_used, insertion_index, current_context = generate_context( @@ -107,7 +107,7 @@ def chat_with_ai( # assert tokens_remaining >= 0, "Tokens remaining is negative. This should never happen, please submit a bug report at https://www.github.com/Torantulino/Auto-GPT" # Debug print the current context - if debug: + if cfg.debug_mode: print(f"Token limit: {token_limit}") print(f"Send Token Count: {current_tokens_used}") print(f"Tokens remaining for response: {tokens_remaining}") diff --git a/scripts/config.py b/scripts/config.py index fe48d29800..a7c980e1a4 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -28,6 +28,7 @@ class Config(metaclass=Singleton): def __init__(self): self.continuous_mode = False self.speak_mode = False + self.debug_mode = False # TODO - make these models be self-contained, using langchain, so we can configure them once and call it good self.fast_llm_model = os.getenv("FAST_LLM_MODEL", "gpt-3.5-turbo") self.smart_llm_model = os.getenv("SMART_LLM_MODEL", "gpt-4") @@ -66,6 +67,9 @@ class Config(metaclass=Singleton): def set_speak_mode(self, value: bool): self.speak_mode = value + def set_debug_mode(self, value: bool): + self.debug_mode = value + def set_fast_llm_model(self, value: str): self.fast_llm_model = value diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 8ec9238b4d..518ed97fd8 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -61,7 +61,7 @@ def fix_json(json_str: str, schema: str, debug=False) -> str: result_string = call_ai_function( function_string, args, description_string, model=cfg.fast_llm_model ) - if debug: + if cfg.debug_mode: print("------------ JSON FIX ATTEMPT ---------------") print(f"Original JSON: {json_str}") print("-----------") diff --git a/scripts/main.py b/scripts/main.py index 17385bf339..04b2255fd2 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -262,6 +262,10 @@ def parse_arguments(): print_to_console("Speak Mode: ", Fore.GREEN, "ENABLED") cfg.set_speak_mode(True) + if args.debug: + print_to_console("Debug Mode: ", Fore.GREEN, "ENABLED") + cfg.set_debug_mode(True) + if args.gpt3only: print_to_console("GPT3.5 Only Mode: ", Fore.GREEN, "ENABLED") cfg.set_smart_llm_model(cfg.fast_llm_model) From 0de94b11cda94a3eceb1a632f234d35f4ecdc813 Mon Sep 17 00:00:00 2001 From: kinance Date: Sat, 8 Apr 2023 12:57:58 +0900 Subject: [PATCH 059/130] Revert .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 956242a45b..6f1c61296a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,3 @@ auto_gpt_workspace/* .env outputs/* ai_settings.yaml -.venv/* \ No newline at end of file From c4c7350670ac378374b76ec2861ee28b88adb462 Mon Sep 17 00:00:00 2001 From: kinance Date: Sat, 8 Apr 2023 13:05:32 +0900 Subject: [PATCH 060/130] Revised the debug mode --- .gitignore | 2 +- scripts/chat.py | 3 +-- scripts/json_parser.py | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 6f1c61296a..7091a87237 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ auto_gpt_workspace/* *.mpeg .env outputs/* -ai_settings.yaml +ai_settings.yaml \ No newline at end of file diff --git a/scripts/chat.py b/scripts/chat.py index 6ad5f47898..505c061e5a 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -43,8 +43,7 @@ def chat_with_ai( user_input, full_message_history, permanent_memory, - token_limit, - debug=False): + token_limit): while True: try: """ diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 518ed97fd8..4a6f284591 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -40,7 +40,7 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): if try_to_fix_with_gpt: print(f"Warning: Failed to parse AI output, attempting to fix.\n If you see this warning frequently, it's likely that your prompt is confusing the AI. Try changing it up slightly.") # Now try to fix this up using the ai_functions - ai_fixed_json = fix_json(json_str, json_schema, False) + ai_fixed_json = fix_json(json_str, json_schema) if ai_fixed_json != "failed": return json.loads(ai_fixed_json) else: @@ -49,7 +49,7 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): else: raise e -def fix_json(json_str: str, schema: str, debug=False) -> str: +def fix_json(json_str: str, schema: str) -> str: # Try to fix the JSON using gpt: function_string = "def fix_json(json_str: str, schema:str=None) -> str:" args = [f"'''{json_str}'''", f"'''{schema}'''"] From cc05139843853ab2ac091c38253a810e418e675c Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Sat, 8 Apr 2023 05:24:53 +0100 Subject: [PATCH 061/130] Update README.md --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4fc0c349e5..6e92e6b953 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Auto-GPT is an experimental open-source application showcasing the capabilities https://user-images.githubusercontent.com/22963551/228855501-2f5777cf-755b-4407-a643-c7299e5b6419.mp4 -## πŸ’– Help Fund Auto-GPT's Development +

πŸ’– Help Fund Auto-GPT's Development πŸ’–

If you can spare a coffee, you can help to cover the API costs of developing Auto-GPT and help push the boundaries of fully autonomous AI! A full day of development can easily cost as much as $20 in API costs, which for a free project is quite limiting. @@ -17,14 +17,13 @@ Your support is greatly appreciated

- Development of this free, open-source project is made possible by all the contributors and sponsors. If you'd like to sponsor this project and have your avatar or company logo appear below click here. πŸ’– -

-

-thepok  SpacingLily  m  zkonduit  maxxflyer  tekelsey  nocodeclarity  tjarmain  alexisneuhaus  jaumebalust  robinicus  digisomni   -

+ Development of this free, open-source project is made possible by all the contributors and sponsors. If you'd like to sponsor this project and have your avatar or company logo appear below click here. +

Individual Sponsors

-alexisneuhaus  iokode  jaumebalust  nova-land  robinicus  Void-n-Null  ritesh24  merwanehamadi  raulmarindev  siduppal  goosecubedaddy  pleabargain   +robinicus  prompthero  crizzler  tob-le-rone  FSTatSBS  toverly1  ddtarazona  Nalhos  Kazamario  pingbotan  indoor47  AuroraHolding  kreativai  hunteraraujo  Explorergt92  judegomila   +thepok +  SpacingLily  merwanehamadi  m  zkonduit  maxxflyer  tekelsey  digisomni  nocodeclarity  tjarmain

From 8b36a5cfd33e38641d594bba10c6fe7356438938 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Sat, 8 Apr 2023 12:27:05 +0100 Subject: [PATCH 062/130] Removes comment --- scripts/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/commands.py b/scripts/commands.py index bf8d79833f..a45fb8963f 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -103,7 +103,7 @@ def execute_command(command_name, arguments): return ai.write_tests(arguments["code"], arguments.get("focus")) elif command_name == "execute_python_file": # Add this command return execute_python_file(arguments["file"]) - elif command_name == "generate_image": # Add this command + elif command_name == "generate_image": return generate_image(arguments["prompt"]) elif command_name == "task_complete": shutdown() From 85d0d27045c2f426fc9618fa8a96b7c89fbdf82a Mon Sep 17 00:00:00 2001 From: "Jonathan S. Rouach" Date: Sat, 8 Apr 2023 15:20:10 +0300 Subject: [PATCH 063/130] fix: add Pillow dependency --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index ce24709858..7b1040401b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ docker duckduckgo-search google-api-python-client #(https://developers.google.com/custom-search/v1/overview) pinecone-client==2.2.1 +Pillow From 3f66a6a0a307d015f166e213d56698bfa92cefcc Mon Sep 17 00:00:00 2001 From: Bill Morgan Date: Fri, 7 Apr 2023 15:36:03 -0500 Subject: [PATCH 064/130] fix typo in prompt.txt --- scripts/data/prompt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/data/prompt.txt b/scripts/data/prompt.txt index 363342c071..77a449de52 100644 --- a/scripts/data/prompt.txt +++ b/scripts/data/prompt.txt @@ -18,7 +18,7 @@ COMMANDS: 12. Append to file: "append_to_file", args: "file": "", "text": "" 13. Delete file: "delete_file", args: "file": "" 14. Search Files: "search_files", args: "directory": "" -15. Evaluate Code: "evaluate_code", args: "code": "" +15. Evaluate Code: "evaluate_code", args: "code": "" 16. Get Improved Code: "improve_code", args: "suggestions": "", "code": "" 17. Write Tests: "write_tests", args: "code": "", "focus": "" 18. Execute Python File: "execute_python_file", args: "file": "" From 7cba76228e0db94aa2b4de6e63096c3f7e65fe62 Mon Sep 17 00:00:00 2001 From: Bill Morgan Date: Sat, 8 Apr 2023 08:22:27 -0500 Subject: [PATCH 065/130] fix command error check --- scripts/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index 17385bf339..d36f979f67 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -358,7 +358,7 @@ while True: f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}") # Execute command - if command_name.lower() == "error": + if command_name.lower().startswith( "error" ): result = f"Command {command_name} threw the following error: " + arguments elif command_name == "human_feedback": result = f"Human feedback: {user_input}" From 47b097708c4916ba8d506efeba905f835410e12d Mon Sep 17 00:00:00 2001 From: Dr33dM3 <109325141+Dr33dM3@users.noreply.github.com> Date: Sat, 8 Apr 2023 18:28:09 -0700 Subject: [PATCH 066/130] Update README.md Changed the wording for the VM to make it more user friendly --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 226306739b..f7bea06256 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ If you don't have access to the GPT4 api, this mode will allow you to use Auto-G ``` python scripts/main.py --gpt3only ``` -For your safety we recommend using a VM(Virtual Machine) so your device may not get bricked as continous mode can potenially be dangerous for your computer. +It is recommended to use a virtual machine for tasks that require high security measures to prevent any potential harm to the main computer's system and data. ## ⚠️ Limitations This experiment aims to showcase the potential of GPT-4 but comes with some limitations: From d1777e39a8668674d40b06f3e3690e68e5daa27d Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Sun, 9 Apr 2023 02:31:51 +0100 Subject: [PATCH 067/130] Fixes incorrect class names in __all__ Changes "Cache" to "Memory". --- scripts/memory/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/memory/__init__.py b/scripts/memory/__init__.py index dacb05b328..9952307943 100644 --- a/scripts/memory/__init__.py +++ b/scripts/memory/__init__.py @@ -37,6 +37,6 @@ def get_memory(cfg, init=False): __all__ = [ "get_memory", "LocalCache", - "RedisCache", - "PineconeCache", + "RedisMemory", + "PineconeMemory", ] From 47c6117e1886d9b69f95807df1e4ee6a2c76eb64 Mon Sep 17 00:00:00 2001 From: Ryan Peach Date: Sat, 8 Apr 2023 22:59:28 -0400 Subject: [PATCH 068/130] Added time and date to the system message for each context --- scripts/chat.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/chat.py b/scripts/chat.py index 8da074c6bf..0b110bbae9 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -26,7 +26,10 @@ def create_chat_message(role, content): def generate_context(prompt, relevant_memory, full_message_history, model): current_context = [ create_chat_message( - "system", prompt), create_chat_message( + "system", prompt), + create_chat_message( + "system", f"The current time and date is {time.strftime('%c')}"), + create_chat_message( "system", f"Permanent memory: {relevant_memory}")] # Add messages from the full message history until we reach the token limit @@ -95,7 +98,7 @@ def chat_with_ai( # Count the currently used tokens current_tokens_used += tokens_to_add - + # Move to the next most recent message in the full message history next_message_to_add_index -= 1 From 2db7f0815eed6e96b94423c96a40b95fc47750d4 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Sat, 8 Apr 2023 22:25:59 -0500 Subject: [PATCH 069/130] Update main.py Remove pinecone config requirement --- scripts/main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index e16fb9d14d..10f9d0dcaa 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -281,8 +281,6 @@ next_action_count = 0 # Make a constant: user_input = "Determine which next command to use, and respond using the format specified above:" -# raise an exception if pinecone_api_key or region is not provided -if not cfg.pinecone_api_key or not cfg.pinecone_region: raise Exception("Please provide pinecone_api_key and pinecone_region") # Initialize memory and make sure it is empty. # this is particularly important for indexing and referencing pinecone memory memory = get_memory(cfg, init=True) From 9e139fb314b7b5c9b538a85d204ff08ce59e10bd Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Sun, 9 Apr 2023 05:22:03 +0100 Subject: [PATCH 070/130] Wipe local memory on load --- scripts/memory/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/memory/__init__.py b/scripts/memory/__init__.py index 9952307943..a441a46aa9 100644 --- a/scripts/memory/__init__.py +++ b/scripts/memory/__init__.py @@ -31,6 +31,8 @@ def get_memory(cfg, init=False): if memory is None: memory = LocalCache(cfg) + if init: + memory.clear() return memory From a861dec6764254b581d23d4573f1da8307bf533a Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Sat, 8 Apr 2023 23:33:18 -0500 Subject: [PATCH 071/130] Memory fixes. --- scripts/memory/local.py | 3 +++ scripts/memory/redismem.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/memory/local.py b/scripts/memory/local.py index fb10522426..8dc90021ff 100644 --- a/scripts/memory/local.py +++ b/scripts/memory/local.py @@ -44,6 +44,8 @@ class LocalCache(MemoryProviderSingleton): Returns: None """ + if 'Command Error:' in text: + return "" self.data.texts.append(text) embedding = get_ada_embedding(text) @@ -64,6 +66,7 @@ class LocalCache(MemoryProviderSingleton): option=SAVE_OPTIONS ) f.write(out) + return text def clear(self) -> str: """ diff --git a/scripts/memory/redismem.py b/scripts/memory/redismem.py index 296d0cce2c..2082fe5887 100644 --- a/scripts/memory/redismem.py +++ b/scripts/memory/redismem.py @@ -69,6 +69,8 @@ class RedisMemory(MemoryProviderSingleton): Returns: Message indicating that the data has been added. """ + if 'Command Error:' in data: + return "" vector = get_ada_embedding(data) vector = np.array(vector).astype(np.float32).tobytes() data_dict = { @@ -132,7 +134,7 @@ class RedisMemory(MemoryProviderSingleton): except Exception as e: print("Error calling Redis search: ", e) return None - return list(results.docs) + return [result.data for result in results.docs] def get_stats(self): """ From d8410d9ca308d2430a7928b61db002046e293b17 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Sun, 9 Apr 2023 06:16:42 +0100 Subject: [PATCH 072/130] Makes it clearer to the AI exactly what memories are. --- scripts/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/chat.py b/scripts/chat.py index 8da074c6bf..5ab52f993e 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -27,7 +27,7 @@ def generate_context(prompt, relevant_memory, full_message_history, model): current_context = [ create_chat_message( "system", prompt), create_chat_message( - "system", f"Permanent memory: {relevant_memory}")] + "system", f"This reminds you of these events from your past:\n{relevant_memory}\n\n")] # Add messages from the full message history until we reach the token limit next_message_to_add_index = len(full_message_history) - 1 From a2fe619c7b7352b9aab35bc10b12f94946712282 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Sun, 9 Apr 2023 06:44:10 +0100 Subject: [PATCH 073/130] Improves response to AI that sends wrong output. --- scripts/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/commands.py b/scripts/commands.py index 1f255751c0..ba5383957a 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -109,7 +109,7 @@ def execute_command(command_name, arguments): elif command_name == "task_complete": shutdown() else: - return f"Unknown command {command_name}" + return f"Unknown command '{command_name}'. Please refer to the 'COMMANDS' list for availabe commands and only respond in the specified JSON format." # All errors, return "Error: + error message" except Exception as e: return "Error: " + str(e) From 97711584c3d9cb904a42974f8d3879af9fd9431c Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Sun, 9 Apr 2023 07:36:00 +0100 Subject: [PATCH 074/130] Update README to indicate Python 3.8 minimum Due to tiktoken dependency. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 760e62cf59..7154446e2a 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Your support is greatly appreciated - πŸ—ƒοΈ File storage and summarization with GPT-3.5 ## πŸ“‹ Requirements -- [Python 3.7 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows) +- [Python 3.8 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows) - OpenAI API key - PINECONE API key From 3efdb4896166c38ff70b2f208c4a644774c1c92b Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Sun, 9 Apr 2023 02:38:06 -0500 Subject: [PATCH 075/130] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 760e62cf59..61181ebf97 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ Run: ``` docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest ``` +See https://hub.docker.com/r/redis/redis-stack-server for setting a password and additional configuration. Set the following environment variables: ``` From 56edfc64401feb8fbe3cdfd8f1e2e4269f2b1db5 Mon Sep 17 00:00:00 2001 From: onekum <55006697+onekum@users.noreply.github.com> Date: Sun, 9 Apr 2023 04:28:23 -0400 Subject: [PATCH 076/130] Tell AI about the `do_nothing` command --- scripts/data/prompt.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/data/prompt.txt b/scripts/data/prompt.txt index 77a449de52..582cf5d3ac 100644 --- a/scripts/data/prompt.txt +++ b/scripts/data/prompt.txt @@ -24,6 +24,7 @@ COMMANDS: 18. Execute Python File: "execute_python_file", args: "file": "" 19. Task Complete (Shutdown): "task_complete", args: "reason": "" 20. Generate Image: "generate_image", args: "prompt": "" +21. Do Nothing; command name: "do_nothing", args: "" RESOURCES: From 93a92d92fc56ebe49758b2e8bb0e9f0eba2f6513 Mon Sep 17 00:00:00 2001 From: onekum <55006697+onekum@users.noreply.github.com> Date: Sun, 9 Apr 2023 04:28:36 -0400 Subject: [PATCH 077/130] make `do_nothing` a valid command --- scripts/commands.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/commands.py b/scripts/commands.py index ba5383957a..02f3baa8df 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -106,6 +106,8 @@ def execute_command(command_name, arguments): return execute_python_file(arguments["file"]) elif command_name == "generate_image": return generate_image(arguments["prompt"]) + elif command_name == "do_nothing": + return "No action performed." elif command_name == "task_complete": shutdown() else: @@ -283,4 +285,4 @@ def delete_agent(key): result = agents.delete_agent(key) if not result: return f"Agent {key} does not exist." - return f"Agent {key} deleted." \ No newline at end of file + return f"Agent {key} deleted." From 287a76b03c36e3cd8458c7dc6e414fd71b8259fc Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Sun, 9 Apr 2023 10:04:03 +0100 Subject: [PATCH 078/130] add basic logging --- scripts/main.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/main.py b/scripts/main.py index 10f9d0dcaa..61d178d739 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -16,7 +16,15 @@ from ai_config import AIConfig import traceback import yaml import argparse +import logging +def configure_logging(): + logging.basicConfig(filename='log.txt', + filemode='a', + format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=logging.DEBUG) + return logging.getLogger('AutoGPT') def print_to_console( title, @@ -26,10 +34,12 @@ def print_to_console( min_typing_speed=0.05, max_typing_speed=0.01): global cfg + global logger if speak_text and cfg.speak_mode: speak.say_text(f"{title}. {content}") print(title_color + title + " " + Style.RESET_ALL, end="") if content: + logger.info(title + ': ' + content) if isinstance(content, list): content = " ".join(content) words = content.split() @@ -270,6 +280,7 @@ def parse_arguments(): # TODO: fill in llm values here cfg = Config() +logger = configure_logging() parse_arguments() ai_name = "" prompt = construct_prompt() From b87d126e324059db3b47863dbe8e5d852234196b Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Sun, 9 Apr 2023 10:06:30 +0100 Subject: [PATCH 079/130] update print_to_console to log to a text file Uses python logging module to output progress to a text file log.txt --- scripts/main.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index 10f9d0dcaa..d9b7204c25 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -16,7 +16,15 @@ from ai_config import AIConfig import traceback import yaml import argparse +import logging +def configure_logging(): + logging.basicConfig(filename='log.txt', + filemode='a', + format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=logging.DEBUG) + return logging.getLogger('AutoGPT') def print_to_console( title, @@ -26,10 +34,12 @@ def print_to_console( min_typing_speed=0.05, max_typing_speed=0.01): global cfg + global logger if speak_text and cfg.speak_mode: speak.say_text(f"{title}. {content}") print(title_color + title + " " + Style.RESET_ALL, end="") if content: + logger.info(title + ': ' + content) if isinstance(content, list): content = " ".join(content) words = content.split() @@ -270,6 +280,7 @@ def parse_arguments(): # TODO: fill in llm values here cfg = Config() +logger = configure_logging() parse_arguments() ai_name = "" prompt = construct_prompt() @@ -355,7 +366,7 @@ while True: f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}") # Execute command - if command_name.lower() == "error": + if command_name.lower().startswith( "error" ): result = f"Command {command_name} threw the following error: " + arguments elif command_name == "human_feedback": result = f"Human feedback: {user_input}" @@ -381,3 +392,4 @@ while True: "system", "Unable to execute command")) print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") + From 54e228504f6c16fe2bc0772d9b29358f2420adbd Mon Sep 17 00:00:00 2001 From: Weltolk <40228052+Weltolk@users.noreply.github.com> Date: Sun, 9 Apr 2023 18:05:50 +0800 Subject: [PATCH 080/130] Fix Elevenlabs link error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba80818d0e..66c95eca3a 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ pip install -r requirements.txt 4. Rename `.env.template` to `.env` and fill in your `OPENAI_API_KEY`. If you plan to use Speech Mode, fill in your `ELEVEN_LABS_API_KEY` as well. - Obtain your OpenAI API key from: https://platform.openai.com/account/api-keys. - - Obtain your ElevenLabs API key from: https://elevenlabs.io. You can view your xi-api-key using the "Profile" tab on the website. + - Obtain your ElevenLabs API key from: https://beta.elevenlabs.io. You can view your xi-api-key using the "Profile" tab on the website. - If you want to use GPT on an Azure instance, set `USE_AZURE` to `True` and provide the `OPENAI_API_BASE`, `OPENAI_API_VERSION` and `OPENAI_DEPLOYMENT_ID` values as explained here: https://pypi.org/project/openai/ in the `Microsoft Azure Endpoints` section ## πŸ”§ Usage From 546d8783e78096d737351fca00d2cd701b9b72e5 Mon Sep 17 00:00:00 2001 From: Alexander Nikulin Date: Sun, 9 Apr 2023 14:33:30 +0400 Subject: [PATCH 081/130] put debug setting to cfg and use it in when calling chat.chat_with_at and fix_json --- scripts/config.py | 4 ++++ scripts/json_parser.py | 2 +- scripts/main.py | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/config.py b/scripts/config.py index d5f1a3f066..4d7adec1c0 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -31,6 +31,7 @@ class Config(metaclass=Singleton): """ def __init__(self): + self.debug = False self.continuous_mode = False self.speak_mode = False # TODO - make these models be self-contained, using langchain, so we can configure them once and call it good @@ -110,3 +111,6 @@ class Config(metaclass=Singleton): def set_pinecone_region(self, value: str): self.pinecone_region = value + + def set_debug_mode(self, value: bool): + self.debug = value diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 8ec9238b4d..c863ccdbb0 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -40,7 +40,7 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): if try_to_fix_with_gpt: print(f"Warning: Failed to parse AI output, attempting to fix.\n If you see this warning frequently, it's likely that your prompt is confusing the AI. Try changing it up slightly.") # Now try to fix this up using the ai_functions - ai_fixed_json = fix_json(json_str, json_schema, False) + ai_fixed_json = fix_json(json_str, json_schema, cfg.debug) if ai_fixed_json != "failed": return json.loads(ai_fixed_json) else: diff --git a/scripts/main.py b/scripts/main.py index a0a1898cc4..f96afeb163 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -266,6 +266,10 @@ def parse_arguments(): print_to_console("GPT3.5 Only Mode: ", Fore.GREEN, "ENABLED") cfg.set_smart_llm_model(cfg.fast_llm_model) + if args.debug: + print_to_console("Debug Mode: ", Fore.GREEN, "ENABLED") + cfg.set_debug_mode(True) + # TODO: fill in llm values here @@ -295,7 +299,7 @@ while True: user_input, full_message_history, memory, - cfg.fast_token_limit) # TODO: This hardcodes the model to use GPT3.5. Make this an argument + cfg.fast_token_limit, cfg.debug) # TODO: This hardcodes the model to use GPT3.5. Make this an argument # Print Assistant thoughts print_assistant_thoughts(assistant_reply) From 011699e6a1df18254d08f6ec0b0d6b482e02e690 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sun, 9 Apr 2023 15:39:11 +0200 Subject: [PATCH 082/130] Code review changes --- .gitignore | 2 +- Dockerfile | 2 +- scripts/ai_config.py | 2 -- scripts/ai_functions.py | 6 +++--- scripts/browse.py | 12 +++--------- scripts/call_ai_function.py | 4 ++-- scripts/chat.py | 14 ++++---------- scripts/commands.py | 4 ++-- scripts/config.py | 12 +----------- scripts/data.py | 4 ++-- scripts/execute_code.py | 6 +++--- scripts/file_operations.py | 2 -- scripts/json_parser.py | 6 ------ scripts/llm_utils.py | 1 - scripts/main.py | 36 ++++-------------------------------- scripts/speak.py | 4 ++-- scripts/spinner.py | 3 --- scripts/token_counter.py | 6 ------ 18 files changed, 28 insertions(+), 98 deletions(-) diff --git a/.gitignore b/.gitignore index fc20dfd299..6b8f00b511 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ scripts/auto_gpt_workspace/* *.mpeg .env last_run_ai_settings.yaml -outputs/* +outputs/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d2a127c7f1..c67291c88d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.11 WORKDIR /app COPY scripts/ /app -COPY requirements.txt /app/requirements.txt + RUN pip install -r requirements.txt CMD ["python", "main.py"] diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 0b521b7dce..de214463b5 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -13,7 +13,6 @@ class AIConfig: # Soon this will go in a folder where it remembers more stuff about the run(s) SAVE_FILE = "last_run_ai_settings.yaml" - @classmethod def load(cls, config_file=SAVE_FILE): """Load variables from yaml file if it exists, otherwise use defaults.""" @@ -29,7 +28,6 @@ class AIConfig: return cls(ai_name, ai_role, ai_goals) - def save(self, config_file=SAVE_FILE): """Save variables to yaml file.""" config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals} diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index eb6dbd935c..f93d7ea693 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -1,5 +1,5 @@ from typing import List, Optional -from json import dumps +import json from config import Config from call_ai_function import call_ai_function from json_parser import fix_and_parse_json @@ -23,7 +23,7 @@ def improve_code(suggestions: List[str], code: str) -> str: function_string = ( "def generate_improved_code(suggestions: List[str], code: str) -> str:" ) - args = [dumps(suggestions), code] + args = [json.dumps(suggestions), code] description_string = """Improves the provided code based on the suggestions provided, making no other changes.""" result_string = call_ai_function(function_string, args, description_string) @@ -36,7 +36,7 @@ def write_tests(code: str, focus: List[str]) -> str: function_string = ( "def create_test_cases(code: str, focus: Optional[str] = None) -> str:" ) - args = [code, dumps(focus)] + args = [code, json.dumps(focus)] description_string = """Generates test cases for the existing code, focusing on specific areas if required.""" result_string = call_ai_function(function_string, args, description_string) diff --git a/scripts/browse.py b/scripts/browse.py index 3fde8d25d3..89e41af496 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -1,14 +1,13 @@ -from requests import get +import requests from bs4 import BeautifulSoup from config import Config from llm_utils import create_chat_completion cfg = Config() - def scrape_text(url): """Scrape text from a webpage""" - response = get(url) + response = requests.get(url) # Check if the response contains an HTTP error if response.status_code >= 400: @@ -30,26 +29,22 @@ def scrape_text(url): def extract_hyperlinks(soup): """Extract hyperlinks from a BeautifulSoup object""" hyperlinks = [] - for link in soup.find_all('a', href=True): hyperlinks.append((link.text, link['href'])) - return hyperlinks def format_hyperlinks(hyperlinks): """Format hyperlinks into a list of strings""" formatted_links = [] - for link_text, link_url in hyperlinks: formatted_links.append(f"{link_text} ({link_url})") - return formatted_links def scrape_links(url): """Scrape hyperlinks from a webpage""" - response = get(url) + response = requests.get(url) # Check if the response contains an HTTP error if response.status_code >= 400: @@ -72,7 +67,6 @@ def split_text(text, max_length=8192): current_chunk = [] for paragraph in paragraphs: - if current_length + len(paragraph) + 1 <= max_length: current_chunk.append(paragraph) current_length += len(paragraph) + 1 diff --git a/scripts/call_ai_function.py b/scripts/call_ai_function.py index 44f9fd8cac..82cf1a76dc 100644 --- a/scripts/call_ai_function.py +++ b/scripts/call_ai_function.py @@ -1,8 +1,8 @@ from config import Config -from llm_utils import create_chat_completion + cfg = Config() - +from llm_utils import create_chat_completion # This is a magic function that can do anything with no-code. See # https://github.com/Torantulino/AI-Functions for more info. def call_ai_function(function, args, description, model=cfg.smart_llm_model): diff --git a/scripts/chat.py b/scripts/chat.py index 17e36879ed..89a0350990 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -1,11 +1,11 @@ -from time import sleep +import time import openai from dotenv import load_dotenv from config import Config import token_counter from llm_utils import create_chat_completion -cfg = Config() +cfg = Config() def create_chat_message(role, content): """ @@ -48,10 +48,8 @@ def chat_with_ai( """ model = cfg.fast_llm_model # TODO: Change model from hardcode to argument # Reserve 1000 tokens for the response - if debug: - print(f"Token limit: {token_limit}") - + print(f"Token limit: {token_limit}") send_token_limit = token_limit - 1000 current_context = [ @@ -73,7 +71,6 @@ def chat_with_ai( message_to_add = full_message_history[next_message_to_add_index] tokens_to_add = token_counter.count_message_tokens([message_to_add], model) - if current_tokens_used + tokens_to_add > send_token_limit: break @@ -99,16 +96,13 @@ def chat_with_ai( print(f"Send Token Count: {current_tokens_used}") print(f"Tokens remaining for response: {tokens_remaining}") print("------------ CONTEXT SENT TO AI ---------------") - for message in current_context: # Skip printing the prompt - if message["role"] == "system" and message["content"] == prompt: continue print( f"{message['role'].capitalize()}: {message['content']}") print() - print("----------- END OF CONTEXT ----------------") # TODO: use a model defined elsewhere, so that model can contain temperature and other settings we care about @@ -130,4 +124,4 @@ def chat_with_ai( except openai.error.RateLimitError: # TODO: WHen we switch to langchain, this is built in print("Error: ", "API Rate Limit Reached. Waiting 10 seconds...") - sleep(10) + time.sleep(10) diff --git a/scripts/commands.py b/scripts/commands.py index 134b3fd833..38912b4860 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -1,7 +1,7 @@ import browse import json import memory as mem -from datetime import datetime +import datetime import agent_manager as agents import speak from config import Config @@ -110,7 +110,7 @@ def execute_command(command_name, arguments): def get_datetime(): """Return the current date and time""" return "Current date and time: " + \ - datetime.now().strftime("%Y-%m-%d %H:%M:%S") + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") def google_search(query, num_results=8): diff --git a/scripts/config.py b/scripts/config.py index 0650535f68..cf8fe7e7ee 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -44,52 +44,42 @@ class Config(metaclass=Singleton): # Initialize the OpenAI API client openai.api_key = self.openai_api_key - def set_continuous_mode(self, value: bool): """Set the continuous mode value.""" self.continuous_mode = value - def set_speak_mode(self, value: bool): """Set the speak mode value.""" self.speak_mode = value - def set_fast_llm_model(self, value: str): """Set the fast LLM model value.""" self.fast_llm_model = value - def set_smart_llm_model(self, value: str): """Set the smart LLM model value.""" self.smart_llm_model = value - def set_fast_token_limit(self, value: int): """Set the fast token limit value.""" self.fast_token_limit = value - def set_smart_token_limit(self, value: int): """Set the smart token limit value.""" self.smart_token_limit = value - def set_openai_api_key(self, value: str): """Set the OpenAI API key value.""" self.openai_api_key = value - def set_elevenlabs_api_key(self, value: str): """Set the ElevenLabs API key value.""" self.elevenlabs_api_key = value - def set_google_api_key(self, value: str): """Set the Google API key value.""" self.google_api_key = value - def set_custom_search_engine_id(self, value: str): """Set the custom search engine ID value.""" - self.custom_search_engine_id = value + self.custom_search_engine_id = value \ No newline at end of file diff --git a/scripts/data.py b/scripts/data.py index 7694329aaa..e0840dd9f7 100644 --- a/scripts/data.py +++ b/scripts/data.py @@ -1,4 +1,4 @@ -from os import path +import os from pathlib import Path SRC_DIR = Path(__file__).parent @@ -6,7 +6,7 @@ def load_prompt(): """Load the prompt from data/prompt.txt""" try: # get directory of this file: - file_dir = Path(path.dirname(path.realpath(__file__))) + file_dir = Path(os.path.dirname(os.path.realpath(__file__))) data_dir = file_dir / "data" prompt_file = data_dir / "prompt.txt" # Load the promt from data/prompt.txt diff --git a/scripts/execute_code.py b/scripts/execute_code.py index 22ae104746..f34469dda8 100644 --- a/scripts/execute_code.py +++ b/scripts/execute_code.py @@ -1,5 +1,5 @@ import docker -from os import path +import os def execute_python_file(file): @@ -11,9 +11,9 @@ def execute_python_file(file): if not file.endswith(".py"): return "Error: Invalid file type. Only .py files are allowed." - file_path = path.join(workspace_folder, file) + file_path = os.path.join(workspace_folder, file) - if not path.isfile(file_path): + if not os.path.isfile(file_path): return f"Error: File '{file}' does not exist." try: diff --git a/scripts/file_operations.py b/scripts/file_operations.py index cb0546168c..d7596c9170 100644 --- a/scripts/file_operations.py +++ b/scripts/file_operations.py @@ -36,10 +36,8 @@ def write_to_file(filename, text): try: filepath = safe_join(working_directory, filename) directory = os.path.dirname(filepath) - if not os.path.exists(directory): os.makedirs(directory) - with open(filepath, "w") as f: f.write(text) return "File written to successfully." diff --git a/scripts/json_parser.py b/scripts/json_parser.py index e827b75336..e46161ac21 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -1,7 +1,6 @@ import json from call_ai_function import call_ai_function from config import Config - cfg = Config() def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): @@ -38,18 +37,15 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): json_str = json_str[:last_brace_index+1] return json.loads(json_str) except Exception as e: - if try_to_fix_with_gpt: print(f"Warning: Failed to parse AI output, attempting to fix.\n If you see this warning frequently, it's likely that your prompt is confusing the AI. Try changing it up slightly.") # Now try to fix this up using the ai_functions ai_fixed_json = fix_json(json_str, json_schema, False) - if ai_fixed_json != "failed": return json.loads(ai_fixed_json) else: print(f"Failed to fix ai output, telling the AI.") # This allows the AI to react to the error message, which usually results in it correcting its ways. return json_str - else: raise e @@ -63,11 +59,9 @@ def fix_json(json_str: str, schema: str, debug=False) -> str: # If it doesn't already start with a "`", add one: if not json_str.startswith("`"): json_str = "```json\n" + json_str + "\n```" - result_string = call_ai_function( function_string, args, description_string, model=cfg.fast_llm_model ) - if debug: print("------------ JSON FIX ATTEMPT ---------------") print(f"Original JSON: {json_str}") diff --git a/scripts/llm_utils.py b/scripts/llm_utils.py index 2981c8a0a2..c512e99733 100644 --- a/scripts/llm_utils.py +++ b/scripts/llm_utils.py @@ -1,6 +1,5 @@ import openai from config import Config - cfg = Config() openai.api_key = cfg.openai_api_key diff --git a/scripts/main.py b/scripts/main.py index e89e57dcd3..9b93855958 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -27,24 +27,17 @@ def print_to_console( max_typing_speed=0.01): """Prints text to the console with a typing effect""" global cfg - if speak_text and cfg.speak_mode: speak.say_text(f"{title}. {content}") - print(title_color + title + " " + Style.RESET_ALL, end="") - if content: - if isinstance(content, list): content = " ".join(content) words = content.split() - for i, word in enumerate(words): print(word, end="", flush=True) - if i < len(words) - 1: print(" ", end="", flush=True) - typing_speed = random.uniform(min_typing_speed, max_typing_speed) time.sleep(typing_speed) # type faster after each word @@ -88,7 +81,6 @@ def print_assistant_thoughts(assistant_reply): if assistant_thoughts_plan: print_to_console("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): @@ -96,7 +88,6 @@ def print_assistant_thoughts(assistant_reply): # Split the input_string using the newline character and dashes lines = assistant_thoughts_plan.split('\n') - for line in lines: line = line.lstrip("- ") print_to_console("- ", Fore.GREEN, line.strip()) @@ -131,13 +122,11 @@ def load_variables(config_file="config.yaml"): # Prompt the user for input if config file is missing or empty values if not ai_name: ai_name = input("Name your AI: ") - if ai_name == "": ai_name = "Entrepreneur-GPT" if not ai_role: ai_role = input(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." @@ -146,20 +135,16 @@ def load_variables(config_file="config.yaml"): print("For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") print("Enter nothing to load defaults, enter nothing when finished.") ai_goals = [] - for i in range(5): ai_goal = input(f"Goal {i+1}: ") - if ai_goal == "": break ai_goals.append(ai_goal) - if len(ai_goals) == 0: ai_goals = ["Increase net worth", "Grow Twitter Account", "Develop and manage multiple businesses autonomously"] # Save variables to yaml file config = {"ai_name": ai_name, "ai_role": ai_role, "ai_goals": ai_goals} - with open(config_file, "w") as file: documents = yaml.dump(config, file) @@ -168,7 +153,6 @@ def load_variables(config_file="config.yaml"): # Construct full prompt full_prompt = f"You are {ai_name}, {ai_role}\n{prompt_start}\n\nGOALS:\n\n" - for i, goal in enumerate(ai_goals): full_prompt += f"{i+1}. {goal}\n" @@ -179,7 +163,6 @@ def load_variables(config_file="config.yaml"): def construct_prompt(): """Construct the prompt for the AI to respond to""" config = AIConfig.load() - if config.ai_name: print_to_console( f"Welcome back! ", @@ -190,8 +173,7 @@ def construct_prompt(): Name: {config.ai_name} Role: {config.ai_role} Goals: {config.ai_goals} - Continue (y/n): """) - + Continue (y/n): """) if should_continue.lower() == "n": config = AIConfig() @@ -221,10 +203,8 @@ def prompt_user(): print_to_console( "Name your AI: ", Fore.GREEN, - "For example, 'Entrepreneur-GPT'") - + "For example, 'Entrepreneur-GPT'") ai_name = input("AI Name: ") - if ai_name == "": ai_name = "Entrepreneur-GPT" @@ -240,7 +220,6 @@ def prompt_user(): Fore.GREEN, "For example, 'an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth.'") ai_role = input(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." @@ -248,19 +227,14 @@ def prompt_user(): print_to_console( "Enter up to 5 goals for your AI: ", Fore.GREEN, - "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") - + "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") print("Enter nothing to load defaults, enter nothing when finished.", flush=True) ai_goals = [] - for i in range(5): ai_goal = input(f"{Fore.LIGHTBLUE_EX}Goal{Style.RESET_ALL} {i+1}: ") - if ai_goal == "": break - ai_goals.append(ai_goal) - if len(ai_goals) == 0: ai_goals = ["Increase net worth", "Grow Twitter Account", "Develop and manage multiple businesses autonomously"] @@ -268,7 +242,6 @@ def prompt_user(): config = AIConfig(ai_name, ai_role, ai_goals) return config - def parse_arguments(): """Parses the arguments passed to the script""" global cfg @@ -346,8 +319,7 @@ while True: f"Enter 'y' to authorise command or 'n' to exit program, or enter feedback for {ai_name}...", flush=True) while True: - console_input = input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) - + console_input = input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) if console_input.lower() == "y": user_input = "GENERATE NEXT COMMAND JSON" break diff --git a/scripts/speak.py b/scripts/speak.py index 9f32fac277..398eaf597d 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -2,7 +2,6 @@ import os from playsound import playsound import requests from config import Config - cfg = Config() import gtts @@ -20,7 +19,8 @@ def eleven_labs_speech(text, voice_index=0): tts_url = "https://api.elevenlabs.io/v1/text-to-speech/{voice_id}".format( voice_id=voices[voice_index]) formatted_message = {"text": text} - response = requests.post(tts_url, headers=tts_headers, json=formatted_message) + response = requests.post( + tts_url, headers=tts_headers, json=formatted_message) if response.status_code == 200: with open("speech.mpeg", "wb") as f: diff --git a/scripts/spinner.py b/scripts/spinner.py index 2b79ae262b..df39dbbd22 100644 --- a/scripts/spinner.py +++ b/scripts/spinner.py @@ -14,7 +14,6 @@ class Spinner: self.running = False self.spinner_thread = None - def spin(self): """Spin the spinner""" while self.running: @@ -23,14 +22,12 @@ class Spinner: time.sleep(self.delay) sys.stdout.write('\b' * (len(self.message) + 2)) - def __enter__(self): """Start the spinner""" self.running = True self.spinner_thread = threading.Thread(target=self.spin) self.spinner_thread.start() - def __exit__(self, exc_type, exc_value, exc_traceback): """Stop the spinner""" self.running = False diff --git a/scripts/token_counter.py b/scripts/token_counter.py index fc5b9c5118..a28a9868ed 100644 --- a/scripts/token_counter.py +++ b/scripts/token_counter.py @@ -1,7 +1,6 @@ import tiktoken from typing import List, Dict - def count_message_tokens(messages : List[Dict[str, str]], model : str = "gpt-3.5-turbo-0301") -> int: """ Returns the number of tokens used by a list of messages. @@ -18,7 +17,6 @@ def count_message_tokens(messages : List[Dict[str, str]], model : str = "gpt-3.5 except KeyError: print("Warning: model not found. Using cl100k_base encoding.") encoding = tiktoken.get_encoding("cl100k_base") - if model == "gpt-3.5-turbo": # !Node: gpt-3.5-turbo may change over time. Returning num tokens assuming gpt-3.5-turbo-0301.") return count_message_tokens(messages, model="gpt-3.5-turbo-0301") @@ -34,19 +32,15 @@ def count_message_tokens(messages : List[Dict[str, str]], model : str = "gpt-3.5 else: raise NotImplementedError(f"""num_tokens_from_messages() is not implemented for model {model}. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens.""") num_tokens = 0 - for message in messages: num_tokens += tokens_per_message - for key, value in message.items(): num_tokens += len(encoding.encode(value)) - if key == "name": num_tokens += tokens_per_name num_tokens += 3 # every reply is primed with <|start|>assistant<|message|> return num_tokens - def count_string_tokens(string: str, model_name: str) -> int: """ Returns the number of tokens in a text string. From d05146c6c5766a17d7b40b932d3de4056e142123 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sun, 9 Apr 2023 15:48:17 +0200 Subject: [PATCH 083/130] Delete unrelated changes with PR --- scripts/ai_config.py | 3 +-- scripts/main.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 14b4588781..43d88ec343 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -40,8 +40,7 @@ class AIConfig: prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.""" # Construct full prompt - full_prompt = f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n" - + full_prompt = f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n" for i, goal in enumerate(self.ai_goals): full_prompt += f"{i+1}. {goal}\n" diff --git a/scripts/main.py b/scripts/main.py index 0c2a94e062..0a2d244780 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -170,10 +170,10 @@ def construct_prompt(): f"Would you like me to return to being {config.ai_name}?", speak_text=True) should_continue = input(f"""Continue with the last settings? - Name: {config.ai_name} - Role: {config.ai_role} - Goals: {config.ai_goals} - Continue (y/n): """) +Name: {config.ai_name} +Role: {config.ai_role} +Goals: {config.ai_goals} +Continue (y/n): """) if should_continue.lower() == "n": config = AIConfig() From 9105a9c7f862c1dc6901ea12b1fff10e70bb4aad Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sun, 9 Apr 2023 16:31:04 +0200 Subject: [PATCH 084/130] Remove whitespaces --- scripts/ai_config.py | 2 +- scripts/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 43d88ec343..59c7520134 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -40,7 +40,7 @@ class AIConfig: prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.""" # Construct full prompt - full_prompt = f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n" + full_prompt = f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n" for i, goal in enumerate(self.ai_goals): full_prompt += f"{i+1}. {goal}\n" diff --git a/scripts/main.py b/scripts/main.py index 0a2d244780..851108a7df 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -173,7 +173,7 @@ def construct_prompt(): Name: {config.ai_name} Role: {config.ai_role} Goals: {config.ai_goals} -Continue (y/n): """) +Continue (y/n): """) if should_continue.lower() == "n": config = AIConfig() From c4ab35275a2acc585533c044e9d1f168a54d78b3 Mon Sep 17 00:00:00 2001 From: Andres Caicedo Date: Sun, 9 Apr 2023 16:34:36 +0200 Subject: [PATCH 085/130] Remove whitespaces --- scripts/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index 851108a7df..6115cffed1 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -203,7 +203,7 @@ def prompt_user(): print_to_console( "Name your AI: ", Fore.GREEN, - "For example, 'Entrepreneur-GPT'") + "For example, 'Entrepreneur-GPT'") ai_name = input("AI Name: ") if ai_name == "": ai_name = "Entrepreneur-GPT" @@ -227,7 +227,7 @@ def prompt_user(): print_to_console( "Enter up to 5 goals for your AI: ", Fore.GREEN, - "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") + "For example: \nIncrease net worth, Grow Twitter Account, Develop and manage multiple businesses autonomously'") print("Enter nothing to load defaults, enter nothing when finished.", flush=True) ai_goals = [] for i in range(5): @@ -328,7 +328,7 @@ while True: f"Enter 'y' to authorise command, 'y -N' to run N continuous commands, 'n' to exit program, or enter feedback for {ai_name}...", flush=True) while True: - console_input = input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) + console_input = input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) if console_input.lower() == "y": user_input = "GENERATE NEXT COMMAND JSON" break From db3728f6103e661f34de6ae22d54b2ada47bbf03 Mon Sep 17 00:00:00 2001 From: sarango Date: Sun, 9 Apr 2023 12:49:30 -0500 Subject: [PATCH 086/130] ADD .vscode to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7091a87237..804419bf2d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ auto_gpt_workspace/* *.mpeg .env outputs/* -ai_settings.yaml \ No newline at end of file +ai_settings.yaml +.vscode \ No newline at end of file From b5c3d08187dd4df4319a02c33af795eff71884f3 Mon Sep 17 00:00:00 2001 From: openaudible <30847528+openaudible@users.noreply.github.com> Date: Sun, 9 Apr 2023 12:41:28 -0700 Subject: [PATCH 087/130] Update README.md Fix typo to use setx instead of export for setting up pinecone env variable. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba80818d0e..f6bbb7dc2a 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ are loaded for the agent at any given time. For Windows Users: ``` setx PINECONE_API_KEY "YOUR_PINECONE_API_KEY" -export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp +setx PINECONE_ENV "Your pinecone region" # something like: us-east4-gcp ``` For macOS and Linux users: From 54101c79973ca5ca8ccd7e1ac59856cb282c57d8 Mon Sep 17 00:00:00 2001 From: vandervoortj <64353639+vandervoortj@users.noreply.github.com> Date: Sun, 9 Apr 2023 15:49:19 -0400 Subject: [PATCH 088/130] Update .gitignore Ignore auto-get.json --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7091a87237..ce0c33f935 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ auto_gpt_workspace/* *.mpeg .env outputs/* -ai_settings.yaml \ No newline at end of file +ai_settings.yaml +auto-gpt.json From b6444de25dadd44da46acbcd37f3c8358bc18d03 Mon Sep 17 00:00:00 2001 From: BillSchumacher <34168009+BillSchumacher@users.noreply.github.com> Date: Sun, 9 Apr 2023 15:22:55 -0500 Subject: [PATCH 089/130] Fixes for common json errors, cleanup json_parser file. --- scripts/json_parser.py | 82 +++++++++++++++++--------- scripts/json_utils.py | 127 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 28 deletions(-) create mode 100644 scripts/json_utils.py diff --git a/scripts/json_parser.py b/scripts/json_parser.py index c863ccdbb0..6a5f073f3d 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -1,11 +1,13 @@ import json +from typing import Any, Dict, Union from call_ai_function import call_ai_function from config import Config +from json_utils import correct_json + cfg = Config() -def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): - json_schema = """ - { +JSON_SCHEMA = """ +{ "command": { "name": "command name", "args":{ @@ -20,44 +22,68 @@ def fix_and_parse_json(json_str: str, try_to_fix_with_gpt: bool = True): "criticism": "constructive self-criticism", "speak": "thoughts summary to say to user" } - } - """ +} +""" + +def fix_and_parse_json( + json_str: str, + try_to_fix_with_gpt: bool = True +) -> Union[str, Dict[Any, Any]]: try: json_str = json_str.replace('\t', '') return json.loads(json_str) - except Exception as e: - # Let's do something manually - sometimes GPT responds with something BEFORE the braces: - # "I'm sorry, I don't understand. Please try again."{"text": "I'm sorry, I don't understand. Please try again.", "confidence": 0.0} - # So let's try to find the first brace and then parse the rest of the string + except json.JSONDecodeError as _: # noqa: F841 + json_str = correct_json(json_str) try: - brace_index = json_str.index("{") - json_str = json_str[brace_index:] - last_brace_index = json_str.rindex("}") - json_str = json_str[:last_brace_index+1] - return json.loads(json_str) - except Exception as e: - if try_to_fix_with_gpt: - print(f"Warning: Failed to parse AI output, attempting to fix.\n If you see this warning frequently, it's likely that your prompt is confusing the AI. Try changing it up slightly.") + return json.loads(json_str) + except json.JSONDecodeError as _: # noqa: F841 + pass + # Let's do something manually: + # sometimes GPT responds with something BEFORE the braces: + # "I'm sorry, I don't understand. Please try again." + # {"text": "I'm sorry, I don't understand. Please try again.", + # "confidence": 0.0} + # So let's try to find the first brace and then parse the rest + # of the string + try: + brace_index = json_str.index("{") + json_str = json_str[brace_index:] + last_brace_index = json_str.rindex("}") + json_str = json_str[:last_brace_index+1] + return json.loads(json_str) + except json.JSONDecodeError as e: # noqa: F841 + if try_to_fix_with_gpt: + print("Warning: Failed to parse AI output, attempting to fix." + "\n If you see this warning frequently, it's likely that" + " your prompt is confusing the AI. Try changing it up" + " slightly.") # Now try to fix this up using the ai_functions - ai_fixed_json = fix_json(json_str, json_schema, cfg.debug) + ai_fixed_json = fix_json(json_str, JSON_SCHEMA, cfg.debug) if ai_fixed_json != "failed": - return json.loads(ai_fixed_json) + return json.loads(ai_fixed_json) else: - print(f"Failed to fix ai output, telling the AI.") # This allows the AI to react to the error message, which usually results in it correcting its ways. - return json_str - else: + # This allows the AI to react to the error message, + # which usually results in it correcting its ways. + print("Failed to fix ai output, telling the AI.") + return json_str + else: raise e - + + def fix_json(json_str: str, schema: str, debug=False) -> str: # Try to fix the JSON using gpt: function_string = "def fix_json(json_str: str, schema:str=None) -> str:" args = [f"'''{json_str}'''", f"'''{schema}'''"] - description_string = """Fixes the provided JSON string to make it parseable and fully complient with the provided schema.\n If an object or field specifed in the schema isn't contained within the correct JSON, it is ommited.\n This function is brilliant at guessing when the format is incorrect.""" + description_string = "Fixes the provided JSON string to make it parseable"\ + " and fully complient with the provided schema.\n If an object or"\ + " field specifed in the schema isn't contained within the correct"\ + " JSON, it is ommited.\n This function is brilliant at guessing"\ + " when the format is incorrect." # If it doesn't already start with a "`", add one: if not json_str.startswith("`"): - json_str = "```json\n" + json_str + "\n```" + json_str = "```json\n" + json_str + "\n```" result_string = call_ai_function( function_string, args, description_string, model=cfg.fast_llm_model ) @@ -68,11 +94,11 @@ def fix_json(json_str: str, schema: str, debug=False) -> str: print(f"Fixed JSON: {result_string}") print("----------- END OF FIX ATTEMPT ----------------") try: - json.loads(result_string) # just check the validity + json.loads(result_string) # just check the validity return result_string - except: + except: # noqa: E722 # Get the call stack: # import traceback # call_stack = traceback.format_exc() # print(f"Failed to fix JSON: '{json_str}' "+call_stack) - return "failed" \ No newline at end of file + return "failed" diff --git a/scripts/json_utils.py b/scripts/json_utils.py new file mode 100644 index 0000000000..b3ffe4b9ab --- /dev/null +++ b/scripts/json_utils.py @@ -0,0 +1,127 @@ +import re +import json +from config import Config + +cfg = Config() + + +def extract_char_position(error_message: str) -> int: + """Extract the character position from the JSONDecodeError message. + + Args: + error_message (str): The error message from the JSONDecodeError + exception. + + Returns: + int: The character position. + """ + import re + + char_pattern = re.compile(r'\(char (\d+)\)') + if match := char_pattern.search(error_message): + return int(match[1]) + else: + raise ValueError("Character position not found in the error message.") + + +def add_quotes_to_property_names(json_string: str) -> str: + """ + Add quotes to property names in a JSON string. + + Args: + json_string (str): The JSON string. + + Returns: + str: The JSON string with quotes added to property names. + """ + + def replace_func(match): + return f'"{match.group(1)}":' + + property_name_pattern = re.compile(r'(\w+):') + corrected_json_string = property_name_pattern.sub( + replace_func, + json_string) + + try: + json.loads(corrected_json_string) + return corrected_json_string + except json.JSONDecodeError as e: + raise e + + +def balance_braces(json_string: str) -> str: + """ + Balance the braces in a JSON string. + + Args: + json_string (str): The JSON string. + + Returns: + str: The JSON string with braces balanced. + """ + + open_braces_count = json_string.count('{') + close_braces_count = json_string.count('}') + + while open_braces_count > close_braces_count: + json_string += '}' + close_braces_count += 1 + + while close_braces_count > open_braces_count: + json_string = json_string.rstrip('}') + close_braces_count -= 1 + + try: + json.loads(json_string) + return json_string + except json.JSONDecodeError as e: + raise e + + +def fix_invalid_escape(json_str: str, error_message: str) -> str: + while error_message.startswith('Invalid \\escape'): + bad_escape_location = extract_char_position(error_message) + json_str = json_str[:bad_escape_location] + \ + json_str[bad_escape_location + 1:] + try: + json.loads(json_str) + return json_str + except json.JSONDecodeError as e: + if cfg.debug: + print('json loads error - fix invalid escape', e) + error_message = str(e) + return json_str + + +def correct_json(json_str: str) -> str: + """ + Correct common JSON errors. + + Args: + json_str (str): The JSON string. + """ + + try: + if cfg.debug: + print("json", json_str) + json.loads(json_str) + return json_str + except json.JSONDecodeError as e: + if cfg.debug: + print('json loads error', e) + error_message = str(e) + if error_message.startswith('Invalid \\escape'): + json_str = fix_invalid_escape(json_str, error_message) + if error_message.startswith('Expecting property name enclosed in double quotes'): + json_str = add_quotes_to_property_names(json_str) + try: + json.loads(json_str) + return json_str + except json.JSONDecodeError as e: + if cfg.debug: + print('json loads error - add quotes', e) + error_message = str(e) + if balanced_str := balance_braces(json_str): + return balanced_str + return json_str From b0cb247b83d9b0b6c6f7d153ad0dfe076b6327ac Mon Sep 17 00:00:00 2001 From: Itamar Friedman Date: Mon, 10 Apr 2023 00:18:37 +0300 Subject: [PATCH 090/130] scrape_text: added tests + hande RequestException --- scripts/browse.py | 5 +- tests/test_browse_scrape_text.py | 102 +++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 tests/test_browse_scrape_text.py diff --git a/scripts/browse.py b/scripts/browse.py index 0fda3d7b06..40e6ca1fe0 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -6,7 +6,10 @@ from llm_utils import create_chat_completion cfg = Config() def scrape_text(url): - response = requests.get(url, headers=cfg.user_agent_header) + try: + response = requests.get(url, headers=cfg.user_agent_header) + except requests.exceptions.RequestException as e: + return "Error: " + str(e) # Check if the response contains an HTTP error if response.status_code >= 400: diff --git a/tests/test_browse_scrape_text.py b/tests/test_browse_scrape_text.py new file mode 100644 index 0000000000..1a08367e8d --- /dev/null +++ b/tests/test_browse_scrape_text.py @@ -0,0 +1,102 @@ + +# Generated by CodiumAI + +import requests +from unittest.mock import Mock +import pytest + +from scripts.browse import scrape_text + +""" +Code Analysis + +Objective: +The objective of the "scrape_text" function is to scrape the text content from a given URL and return it as a string, after removing any unwanted HTML tags and scripts. + +Inputs: +- url: a string representing the URL of the webpage to be scraped. + +Flow: +1. Send a GET request to the given URL using the requests library and the user agent header from the config file. +2. Check if the response contains an HTTP error. If it does, return an error message. +3. Use BeautifulSoup to parse the HTML content of the response and extract all script and style tags. +4. Get the text content of the remaining HTML using the get_text() method of BeautifulSoup. +5. Split the text into lines and then into chunks, removing any extra whitespace. +6. Join the chunks into a single string with newline characters between them. +7. Return the cleaned text. + +Outputs: +- A string representing the cleaned text content of the webpage. + +Additional aspects: +- The function uses the requests library and BeautifulSoup to handle the HTTP request and HTML parsing, respectively. +- The function removes script and style tags from the HTML to avoid including unwanted content in the text output. +- The function uses a generator expression to split the text into lines and chunks, which can improve performance for large amounts of text. +""" + + + +class TestScrapeText: + + # Tests that scrape_text() returns the expected text when given a valid URL. + def test_scrape_text_with_valid_url(self, mocker): + # Mock the requests.get() method to return a response with expected text + expected_text = "This is some sample text" + mock_response = mocker.Mock() + mock_response.status_code = 200 + mock_response.text = f"

{expected_text}

" + mocker.patch("requests.get", return_value=mock_response) + + # Call the function with a valid URL and assert that it returns the expected text + url = "http://www.example.com" + assert scrape_text(url) == expected_text + + # Tests that the function returns an error message when an invalid or unreachable url is provided. + def test_invalid_url(self, mocker): + # Mock the requests.get() method to raise an exception + mocker.patch("requests.get", side_effect=requests.exceptions.RequestException) + + # Call the function with an invalid URL and assert that it returns an error message + url = "http://www.invalidurl.com" + error_message = scrape_text(url) + assert "Error:" in error_message + + # Tests that the function returns an empty string when the html page contains no text to be scraped. + def test_no_text(self, mocker): + # Mock the requests.get() method to return a response with no text + mock_response = mocker.Mock() + mock_response.status_code = 200 + mock_response.text = "" + mocker.patch("requests.get", return_value=mock_response) + + # Call the function with a valid URL and assert that it returns an empty string + url = "http://www.example.com" + assert scrape_text(url) == "" + + # Tests that the function returns an error message when the response status code is an http error (>=400). + def test_http_error(self, mocker): + # Mock the requests.get() method to return a response with a 404 status code + mocker.patch('requests.get', return_value=Mock(status_code=404)) + + # Call the function with a URL + result = scrape_text("https://www.example.com") + + # Check that the function returns an error message + assert result == "Error: HTTP 404 error" + + # Tests that scrape_text() properly handles HTML tags. + def test_scrape_text_with_html_tags(self): + # Create a mock response object with HTML containing tags + html = "

This is bold text.

" + response = Mock() + response.status_code = 200 + response.text = html + + # Mock the requests.get() method to return the mock response object + requests.get = Mock(return_value=response) + + # Call the function with a URL + result = scrape_text("https://www.example.com") + + # Check that the function properly handles HTML tags + assert result == "This is bold text." \ No newline at end of file From 41f5cd6b389d4999c081691577b6d90087371780 Mon Sep 17 00:00:00 2001 From: Sma Das Date: Sun, 9 Apr 2023 20:53:32 -0400 Subject: [PATCH 091/130] Created `utils.py` --- scripts/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 scripts/utils.py diff --git a/scripts/utils.py b/scripts/utils.py new file mode 100644 index 0000000000..bab9ac9684 --- /dev/null +++ b/scripts/utils.py @@ -0,0 +1,6 @@ +def clean_input(prompt: str=''): + try: + return input(prompt) + except KeyboardInterrupt: + exit(0) + From 9ef82275cad16080e313767fb4e8e424a371efe2 Mon Sep 17 00:00:00 2001 From: Sma Das Date: Sun, 9 Apr 2023 20:55:38 -0400 Subject: [PATCH 092/130] Updated `main.py` to use `clean_input` --- scripts/main.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index f96afeb163..038ed2db83 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -1,6 +1,7 @@ import json import random import commands as cmd +import utils from memory import get_memory import data import chat @@ -119,12 +120,12 @@ def load_variables(config_file="config.yaml"): # Prompt the user for input if config file is missing or empty values if not ai_name: - ai_name = input("Name your AI: ") + ai_name = utils.clean_input("Name your AI: ") if ai_name == "": ai_name = "Entrepreneur-GPT" if not ai_role: - ai_role = input(f"{ai_name} is: ") + ai_role = utils.clean_input(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." @@ -134,7 +135,7 @@ def load_variables(config_file="config.yaml"): print("Enter nothing to load defaults, enter nothing when finished.") ai_goals = [] for i in range(5): - ai_goal = input(f"Goal {i+1}: ") + ai_goal = utils.clean_input(f"Goal {i+1}: ") if ai_goal == "": break ai_goals.append(ai_goal) @@ -166,7 +167,7 @@ def construct_prompt(): Fore.GREEN, f"Would you like me to return to being {config.ai_name}?", speak_text=True) - should_continue = input(f"""Continue with the last settings? + should_continue = utils.clean_input(f"""Continue with the last settings? Name: {config.ai_name} Role: {config.ai_role} Goals: {config.ai_goals} @@ -200,7 +201,7 @@ def prompt_user(): "Name your AI: ", Fore.GREEN, "For example, 'Entrepreneur-GPT'") - ai_name = input("AI Name: ") + ai_name = utils.clean_input("AI Name: ") if ai_name == "": ai_name = "Entrepreneur-GPT" @@ -215,7 +216,7 @@ def prompt_user(): "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 = input(f"{ai_name} is: ") + ai_role = utils.clean_input(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." @@ -227,7 +228,7 @@ def prompt_user(): print("Enter nothing to load defaults, enter nothing when finished.", flush=True) ai_goals = [] for i in range(5): - ai_goal = input(f"{Fore.LIGHTBLUE_EX}Goal{Style.RESET_ALL} {i+1}: ") + ai_goal = utils.clean_input(f"{Fore.LIGHTBLUE_EX}Goal{Style.RESET_ALL} {i+1}: ") if ai_goal == "": break ai_goals.append(ai_goal) @@ -323,7 +324,7 @@ while True: f"Enter 'y' to authorise command, 'y -N' to run N continuous commands, 'n' to exit program, or enter feedback for {ai_name}...", flush=True) while True: - console_input = input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) + console_input = utils.clean_input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) if console_input.lower() == "y": user_input = "GENERATE NEXT COMMAND JSON" break From 34560901175de1b2bd3d7b855c52876b099b59c2 Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 04:21:23 +0200 Subject: [PATCH 093/130] replace gtts with macos tts --- scripts/speak.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/scripts/speak.py b/scripts/speak.py index 13517d366f..eab7b6601f 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -3,11 +3,12 @@ from playsound import playsound import requests from config import Config cfg = Config() -import gtts +# Remove the import of gtts +# import gtts -# TODO: Nicer names for these ids -voices = ["ErXwobaYiN019PkySvjV", "EXAVITQu4vr4xnSDxMaL"] +# Change voices to macOS voice identifiers +voices = ["com.apple.speech.synthesis.voice.siri_female", "com.apple.speech.synthesis.voice.siri_male"] tts_headers = { "Content-Type": "application/json", @@ -32,17 +33,14 @@ def eleven_labs_speech(text, voice_index=0): print("Response content:", response.content) return False -def gtts_speech(text): - tts = gtts.gTTS(text) - tts.save("speech.mp3") - playsound("speech.mp3") - os.remove("speech.mp3") +# Use macOS built-in TTS instead of gtts +def macos_tts_speech(text, voice_index=1): + os.system(f'say "{text}"') def say_text(text, voice_index=0): if not cfg.elevenlabs_api_key: - gtts_speech(text) + macos_tts_speech(text, voice_index) else: success = eleven_labs_speech(text, voice_index) if not success: - gtts_speech(text) - + macos_tts_speech(text, voice_index) From ee1805c13641e576af4af7fb07f39adef5799625 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Mon, 10 Apr 2023 04:21:44 +0100 Subject: [PATCH 094/130] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 33 +++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index cb8ce34a1b..1ac8f8642d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,18 +1,33 @@ -### Background + +Focus on a single, specific change. +Do not include any unrelated or "extra" modifications. +Provide clear documentation and explanations of the changes made. +Ensure diffs are limited to the intended lines β€” no applying preferred formatting styles or line endings (unless that's what the PR is about). +For guidance on committing only the specific lines you have changed, refer to this helpful video: https://youtu.be/8-hSNHHbiZg + +By following these guidelines, your PRs are more likely to be merged quickly after testing, as long as they align with the project's overall direction. --> + +### Background + ### Changes + - +### Documentation + ### Test Plan + - +### PR Quality Checklist +- [ ] My pull request is atomic and focuses on a single change. +- [ ] I have thouroughly tested my changes with multiple different prompts. +- [ ] I have considered potential risks and mitigations for my changes. +- [ ] I have documented my changes clearly and comprehensively. +- [ ] I have not snuck in any "extra" small tweaks changes -### Change Safety + -- [ ] I have added tests to cover my changes -- [ ] I have considered potential risks and mitigations for my changes - - + From 5727b052fe686506971f9d3472c07027116f2b5b Mon Sep 17 00:00:00 2001 From: Jason Drage Date: Mon, 10 Apr 2023 13:38:56 +1000 Subject: [PATCH 095/130] jd: ignore venv --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7091a87237..f602018bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ package-lock.json auto_gpt_workspace/* *.mpeg .env +venv/* outputs/* ai_settings.yaml \ No newline at end of file From 06f26cb29c6980c2c521780c83972fc09db819e4 Mon Sep 17 00:00:00 2001 From: Itamar Friedman Date: Mon, 10 Apr 2023 08:19:41 +0300 Subject: [PATCH 096/130] remove dependency of unittest, use pytest --- tests/test_browse_scrape_text.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/test_browse_scrape_text.py b/tests/test_browse_scrape_text.py index 1a08367e8d..27ebc0f693 100644 --- a/tests/test_browse_scrape_text.py +++ b/tests/test_browse_scrape_text.py @@ -2,7 +2,6 @@ # Generated by CodiumAI import requests -from unittest.mock import Mock import pytest from scripts.browse import scrape_text @@ -76,7 +75,7 @@ class TestScrapeText: # Tests that the function returns an error message when the response status code is an http error (>=400). def test_http_error(self, mocker): # Mock the requests.get() method to return a response with a 404 status code - mocker.patch('requests.get', return_value=Mock(status_code=404)) + mocker.patch('requests.get', return_value=mocker.Mock(status_code=404)) # Call the function with a URL result = scrape_text("https://www.example.com") @@ -85,15 +84,13 @@ class TestScrapeText: assert result == "Error: HTTP 404 error" # Tests that scrape_text() properly handles HTML tags. - def test_scrape_text_with_html_tags(self): + def test_scrape_text_with_html_tags(self, mocker): # Create a mock response object with HTML containing tags html = "

This is bold text.

" - response = Mock() - response.status_code = 200 - response.text = html - - # Mock the requests.get() method to return the mock response object - requests.get = Mock(return_value=response) + mock_response = mocker.Mock() + mock_response.status_code = 200 + mock_response.text = html + mocker.patch("requests.get", return_value=mock_response) # Call the function with a URL result = scrape_text("https://www.example.com") From da4a045bd6aa85805ff30493f7f0b00050a2dc80 Mon Sep 17 00:00:00 2001 From: Itamar Friedman Date: Mon, 10 Apr 2023 08:26:46 +0300 Subject: [PATCH 097/130] Adding most basic URL validation in scrape_text --- scripts/browse.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/browse.py b/scripts/browse.py index 40e6ca1fe0..7eeaaf4d94 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -6,6 +6,10 @@ from llm_utils import create_chat_completion cfg = Config() def scrape_text(url): + # Most basic check if the URL is valid: + if not url.startswith('http'): + return "Error: Invalid URL" + try: response = requests.get(url, headers=cfg.user_agent_header) except requests.exceptions.RequestException as e: From 33c8fe627a58429942e8219b5a79d264ee680ae0 Mon Sep 17 00:00:00 2001 From: pratiksinghchauhan Date: Mon, 10 Apr 2023 11:39:52 +0530 Subject: [PATCH 098/130] improve performance and removed code duplication --- scripts/browse.py | 19 ++++++++----------- scripts/commands.py | 13 +++++++------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/scripts/browse.py b/scripts/browse.py index 0fda3d7b06..70f754e0b6 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -5,14 +5,17 @@ from llm_utils import create_chat_completion cfg = Config() -def scrape_text(url): +def get_website_content(url): response = requests.get(url, headers=cfg.user_agent_header) - # Check if the response contains an HTTP error if response.status_code >= 400: return "Error: HTTP " + str(response.status_code) + " error" + return response - soup = BeautifulSoup(response.text, "html.parser") + + +def scrape_text(website_content): + soup = BeautifulSoup(website_content.text, "html.parser") for script in soup(["script", "style"]): script.extract() @@ -39,14 +42,8 @@ def format_hyperlinks(hyperlinks): return formatted_links -def scrape_links(url): - response = requests.get(url, headers=cfg.user_agent_header) - - # Check if the response contains an HTTP error - if response.status_code >= 400: - return "error" - - soup = BeautifulSoup(response.text, "html.parser") +def scrape_links(website_content): + soup = BeautifulSoup(website_content.text, "html.parser") for script in soup(["script", "style"]): script.extract() diff --git a/scripts/commands.py b/scripts/commands.py index ba5383957a..4f94cebbbe 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -163,8 +163,9 @@ def google_official_search(query, num_results=8): return search_results_links def browse_website(url, question): - summary = get_text_summary(url, question) - links = get_hyperlinks(url) + website_content = browse.get_website_content(url) + summary = get_text_summary(website_content, question) + links = get_hyperlinks(website_content) # Limit links to 5 if len(links) > 5: @@ -175,14 +176,14 @@ def browse_website(url, question): return result -def get_text_summary(url, question): - text = browse.scrape_text(url) +def get_text_summary(website_content, question): + text = browse.scrape_text(website_content) summary = browse.summarize_text(text, question) return """ "Result" : """ + summary -def get_hyperlinks(url): - link_list = browse.scrape_links(url) +def get_hyperlinks(website_content): + link_list = browse.scrape_links(website_content) return link_list From 13467259b4722b45cba098a225609f8e09bbef3f Mon Sep 17 00:00:00 2001 From: pratiksinghchauhan Date: Mon, 10 Apr 2023 12:07:37 +0530 Subject: [PATCH 099/130] fix: #323 Error communicating with OpenAI --- .env.template | 6 +++--- README.md | 2 +- scripts/config.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.env.template b/.env.template index 525cd61c5f..8dcdc37ccb 100644 --- a/.env.template +++ b/.env.template @@ -7,8 +7,8 @@ FAST_LLM_MODEL="gpt-3.5-turbo" GOOGLE_API_KEY= CUSTOM_SEARCH_ENGINE_ID= USE_AZURE=False -OPENAI_API_BASE=your-base-url-for-azure -OPENAI_API_VERSION=api-version-for-azure -OPENAI_DEPLOYMENT_ID=deployment-id-for-azure +OPENAI_AZURE_API_BASE=your-base-url-for-azure +OPENAI_AZURE_API_VERSION=api-version-for-azure +OPENAI_AZURE_DEPLOYMENT_ID=deployment-id-for-azure IMAGE_PROVIDER=dalle HUGGINGFACE_API_TOKEN= \ No newline at end of file diff --git a/README.md b/README.md index ba80818d0e..f581274c5c 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ pip install -r requirements.txt 4. Rename `.env.template` to `.env` and fill in your `OPENAI_API_KEY`. If you plan to use Speech Mode, fill in your `ELEVEN_LABS_API_KEY` as well. - Obtain your OpenAI API key from: https://platform.openai.com/account/api-keys. - Obtain your ElevenLabs API key from: https://elevenlabs.io. You can view your xi-api-key using the "Profile" tab on the website. - - If you want to use GPT on an Azure instance, set `USE_AZURE` to `True` and provide the `OPENAI_API_BASE`, `OPENAI_API_VERSION` and `OPENAI_DEPLOYMENT_ID` values as explained here: https://pypi.org/project/openai/ in the `Microsoft Azure Endpoints` section + - If you want to use GPT on an Azure instance, set `USE_AZURE` to `True` and provide the `OPENAI_AZURE_API_BASE`, `OPENAI_AZURE_API_VERSION` and `OPENAI_AZURE_DEPLOYMENT_ID` values as explained here: https://pypi.org/project/openai/ in the `Microsoft Azure Endpoints` section ## πŸ”§ Usage diff --git a/scripts/config.py b/scripts/config.py index 4d7adec1c0..0e8ea3203b 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -44,9 +44,9 @@ class Config(metaclass=Singleton): self.use_azure = False self.use_azure = os.getenv("USE_AZURE") == 'True' if self.use_azure: - self.openai_api_base = os.getenv("OPENAI_API_BASE") - self.openai_api_version = os.getenv("OPENAI_API_VERSION") - self.openai_deployment_id = os.getenv("OPENAI_DEPLOYMENT_ID") + self.openai_api_base = os.getenv("OPENAI_AZURE_API_BASE") + self.openai_api_version = os.getenv("OPENAI_AZURE_API_VERSION") + self.openai_deployment_id = os.getenv("OPENAI_AZURE_DEPLOYMENT_ID") openai.api_type = "azure" openai.api_base = self.openai_api_base openai.api_version = self.openai_api_version From 156739788aa471304fc5c6eaf9254cd4619fa459 Mon Sep 17 00:00:00 2001 From: pratiksinghchauhan Date: Mon, 10 Apr 2023 12:31:37 +0530 Subject: [PATCH 100/130] removed un necessary changes --- scripts/browse.py | 28 +++++++++++++++++++--------- scripts/commands.py | 15 ++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/scripts/browse.py b/scripts/browse.py index 70f754e0b6..7eeaaf4d94 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -5,17 +5,21 @@ from llm_utils import create_chat_completion cfg = Config() -def get_website_content(url): - response = requests.get(url, headers=cfg.user_agent_header) +def scrape_text(url): + # Most basic check if the URL is valid: + if not url.startswith('http'): + return "Error: Invalid URL" + + try: + response = requests.get(url, headers=cfg.user_agent_header) + except requests.exceptions.RequestException as e: + return "Error: " + str(e) + # Check if the response contains an HTTP error if response.status_code >= 400: return "Error: HTTP " + str(response.status_code) + " error" - return response - - -def scrape_text(website_content): - soup = BeautifulSoup(website_content.text, "html.parser") + soup = BeautifulSoup(response.text, "html.parser") for script in soup(["script", "style"]): script.extract() @@ -42,8 +46,14 @@ def format_hyperlinks(hyperlinks): return formatted_links -def scrape_links(website_content): - soup = BeautifulSoup(website_content.text, "html.parser") +def scrape_links(url): + response = requests.get(url, headers=cfg.user_agent_header) + + # Check if the response contains an HTTP error + if response.status_code >= 400: + return "error" + + soup = BeautifulSoup(response.text, "html.parser") for script in soup(["script", "style"]): script.extract() diff --git a/scripts/commands.py b/scripts/commands.py index 4f94cebbbe..76139b5c62 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -106,6 +106,8 @@ def execute_command(command_name, arguments): return execute_python_file(arguments["file"]) elif command_name == "generate_image": return generate_image(arguments["prompt"]) + elif command_name == "do_nothing": + return "No action performed." elif command_name == "task_complete": shutdown() else: @@ -163,9 +165,8 @@ def google_official_search(query, num_results=8): return search_results_links def browse_website(url, question): - website_content = browse.get_website_content(url) - summary = get_text_summary(website_content, question) - links = get_hyperlinks(website_content) + summary = get_text_summary(url, question) + links = get_hyperlinks(url) # Limit links to 5 if len(links) > 5: @@ -176,14 +177,14 @@ def browse_website(url, question): return result -def get_text_summary(website_content, question): - text = browse.scrape_text(website_content) +def get_text_summary(url, question): + text = browse.scrape_text(url) summary = browse.summarize_text(text, question) return """ "Result" : """ + summary -def get_hyperlinks(website_content): - link_list = browse.scrape_links(website_content) +def get_hyperlinks(url): + link_list = browse.scrape_links(url) return link_list From 9cea7c0f61ce4ec6c5b7b087678f55012c249f45 Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Mon, 10 Apr 2023 08:11:54 +0100 Subject: [PATCH 101/130] Update main.py --- scripts/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index 6ec026d323..2f134e0cc8 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -395,5 +395,5 @@ while True: chat.create_chat_message( "system", "Unable to execute command")) print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") - - + + From 2fb06a3db5051808d5529f48ba074f62c1323c8f Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Mon, 10 Apr 2023 08:14:48 +0100 Subject: [PATCH 102/130] trying to get the right amount of spacing / newlines at the end of main.py! --- scripts/main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index 2f134e0cc8..1bb708c429 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -395,5 +395,3 @@ while True: chat.create_chat_message( "system", "Unable to execute command")) print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") - - From b673302134a72ff8b5975ad62ecbfe4f8d3060e6 Mon Sep 17 00:00:00 2001 From: onekum <55006697+onekum@users.noreply.github.com> Date: Mon, 10 Apr 2023 04:43:34 -0400 Subject: [PATCH 103/130] Make `do_nothing` line uniform --- scripts/data/prompt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/data/prompt.txt b/scripts/data/prompt.txt index 582cf5d3ac..ab281e81a0 100644 --- a/scripts/data/prompt.txt +++ b/scripts/data/prompt.txt @@ -24,7 +24,7 @@ COMMANDS: 18. Execute Python File: "execute_python_file", args: "file": "" 19. Task Complete (Shutdown): "task_complete", args: "reason": "" 20. Generate Image: "generate_image", args: "prompt": "" -21. Do Nothing; command name: "do_nothing", args: "" +21. Do Nothing: "do_nothing", args: "" RESOURCES: From 2facc3e2cb65e72f21e329eb691f445be8599295 Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 11:11:03 +0200 Subject: [PATCH 104/130] add config for mac os tts --- .env.template | 3 ++- scripts/speak.py | 23 ++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.env.template b/.env.template index 525cd61c5f..f9a74c9ca4 100644 --- a/.env.template +++ b/.env.template @@ -11,4 +11,5 @@ OPENAI_API_BASE=your-base-url-for-azure OPENAI_API_VERSION=api-version-for-azure OPENAI_DEPLOYMENT_ID=deployment-id-for-azure IMAGE_PROVIDER=dalle -HUGGINGFACE_API_TOKEN= \ No newline at end of file +HUGGINGFACE_API_TOKEN= +USE_MAC_OS_TTS=False diff --git a/scripts/speak.py b/scripts/speak.py index eab7b6601f..1ffad84641 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -3,12 +3,10 @@ from playsound import playsound import requests from config import Config cfg = Config() +import gtts -# Remove the import of gtts -# import gtts - -# Change voices to macOS voice identifiers -voices = ["com.apple.speech.synthesis.voice.siri_female", "com.apple.speech.synthesis.voice.siri_male"] +# TODO: Nicer names for these ids +voices = ["ErXwobaYiN019PkySvjV", "EXAVITQu4vr4xnSDxMaL"] tts_headers = { "Content-Type": "application/json", @@ -33,14 +31,21 @@ def eleven_labs_speech(text, voice_index=0): print("Response content:", response.content) return False -# Use macOS built-in TTS instead of gtts -def macos_tts_speech(text, voice_index=1): +def gtts_speech(text): + tts = gtts.gTTS(text) + tts.save("speech.mp3") + playsound("speech.mp3") + os.remove("speech.mp3") + +def macos_tts_speech(text): os.system(f'say "{text}"') def say_text(text, voice_index=0): if not cfg.elevenlabs_api_key: - macos_tts_speech(text, voice_index) + if cfg.use_mac_os_tts == 'True': + macos_tts_speech(text) + gtts(text) else: success = eleven_labs_speech(text, voice_index) if not success: - macos_tts_speech(text, voice_index) + gtts_speech()(text) From 205a0c84cf0ba8690a0b49a0a498ceedb5ac987d Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 11:18:17 +0200 Subject: [PATCH 105/130] fix config read --- scripts/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/config.py b/scripts/config.py index 4d7adec1c0..b15d6f909a 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -52,6 +52,8 @@ class Config(metaclass=Singleton): openai.api_version = self.openai_api_version self.elevenlabs_api_key = os.getenv("ELEVENLABS_API_KEY") + + self.use_mac_os_tts = os.getenv("USE_MAC_OS_TTS") self.google_api_key = os.getenv("GOOGLE_API_KEY") self.custom_search_engine_id = os.getenv("CUSTOM_SEARCH_ENGINE_ID") From 64eb882947b09f4ad6c0e024fa327051c9c308be Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 11:20:58 +0200 Subject: [PATCH 106/130] fix code messup of assistant --- scripts/config.py | 1 + scripts/speak.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/config.py b/scripts/config.py index b15d6f909a..e9168efe5d 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -53,6 +53,7 @@ class Config(metaclass=Singleton): self.elevenlabs_api_key = os.getenv("ELEVENLABS_API_KEY") + self.use_mac_os_tts = False self.use_mac_os_tts = os.getenv("USE_MAC_OS_TTS") self.google_api_key = os.getenv("GOOGLE_API_KEY") diff --git a/scripts/speak.py b/scripts/speak.py index 1ffad84641..cdb92e0786 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -44,7 +44,8 @@ def say_text(text, voice_index=0): if not cfg.elevenlabs_api_key: if cfg.use_mac_os_tts == 'True': macos_tts_speech(text) - gtts(text) + else: + gtts(text) else: success = eleven_labs_speech(text, voice_index) if not success: From 1946b564a1d870029c2164914fd815356b780062 Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 11:22:24 +0200 Subject: [PATCH 107/130] fix gtts_speech --- scripts/speak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/speak.py b/scripts/speak.py index cdb92e0786..e5b839fdf1 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -45,7 +45,7 @@ def say_text(text, voice_index=0): if cfg.use_mac_os_tts == 'True': macos_tts_speech(text) else: - gtts(text) + gtts_speech(text) else: success = eleven_labs_speech(text, voice_index) if not success: From ed16bba0ca0ae57c8d9ad1fcaae9555b3d7ed264 Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 11:22:47 +0200 Subject: [PATCH 108/130] fix gtts_speech --- scripts/speak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/speak.py b/scripts/speak.py index e5b839fdf1..485381dc5c 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -49,4 +49,4 @@ def say_text(text, voice_index=0): else: success = eleven_labs_speech(text, voice_index) if not success: - gtts_speech()(text) + gtts_speech(text) From 1f0209bba70c28717888cfa0c2f9f9e633264a7f Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Mon, 10 Apr 2023 12:07:16 +0100 Subject: [PATCH 109/130] Fixe incorrect Docstring --- scripts/speak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/speak.py b/scripts/speak.py index 8a3a64df5a..10dd7c07f4 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -15,7 +15,7 @@ tts_headers = { } def eleven_labs_speech(text, voice_index=0): - """Return the results of a google search""" + """Speak text using elevenlabs.io's API""" tts_url = "https://api.elevenlabs.io/v1/text-to-speech/{voice_id}".format( voice_id=voices[voice_index]) formatted_message = {"text": text} From 0dc7bee345692801ccfc1ac673381d907112850f Mon Sep 17 00:00:00 2001 From: blankster Date: Mon, 10 Apr 2023 13:38:43 +0200 Subject: [PATCH 110/130] Update README.md Added remark regarding daily free Google custom search limit and how to increase it --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ccac19483c..a15e509bcf 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,8 @@ To use the `google_official_search` command, you need to set up your Google API 8. Set up your search engine by following the prompts. You can choose to search the entire web or specific sites. 9. Once you've created your search engine, click on "Control Panel" and then "Basics". Copy the "Search engine ID" and set it as an environment variable named `CUSTOM_SEARCH_ENGINE_ID` on your machine. See setting up environment variables below. +*Remember that your free daily custom search quota is 100 searces. If you want to increase this limit, you need to assign a billing account to the project to profit from up to 10K daily searches.* + ### Setting up environment variables For Windows Users: ``` From 891f8d84c84549c0de31044f5b469299b0cd253e Mon Sep 17 00:00:00 2001 From: blankster Date: Mon, 10 Apr 2023 13:44:36 +0200 Subject: [PATCH 111/130] Update README.md Spelling mistakes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a15e509bcf..b11afb5e1a 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ To use the `google_official_search` command, you need to set up your Google API 8. Set up your search engine by following the prompts. You can choose to search the entire web or specific sites. 9. Once you've created your search engine, click on "Control Panel" and then "Basics". Copy the "Search engine ID" and set it as an environment variable named `CUSTOM_SEARCH_ENGINE_ID` on your machine. See setting up environment variables below. -*Remember that your free daily custom search quota is 100 searces. If you want to increase this limit, you need to assign a billing account to the project to profit from up to 10K daily searches.* +*Remember that your free daily custom search quota allows only up to 100 searches. To increase this limit, you need to assign a billing account to the project to profit from up to 10K daily searches.* ### Setting up environment variables For Windows Users: From ec239734c59dd7b0feb9261fc0d4a8008308c72b Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 14:15:30 +0200 Subject: [PATCH 112/130] fix whitespace --- scripts/speak.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/speak.py b/scripts/speak.py index ed52a433ab..c48d090bd1 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -47,6 +47,7 @@ def say_text(text, voice_index=0): macos_tts_speech(text) else: gtts_speech(text) + else: success = eleven_labs_speech(text, voice_index) if not success: From 7e9941e5b1b560f33e2ad4ed7d05c47af961ec75 Mon Sep 17 00:00:00 2001 From: Wlad Date: Mon, 10 Apr 2023 14:17:18 +0200 Subject: [PATCH 113/130] fix whitespace --- scripts/speak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/speak.py b/scripts/speak.py index c48d090bd1..c47a9f7527 100644 --- a/scripts/speak.py +++ b/scripts/speak.py @@ -47,8 +47,8 @@ def say_text(text, voice_index=0): macos_tts_speech(text) else: gtts_speech(text) - else: success = eleven_labs_speech(text, voice_index) if not success: gtts_speech(text) + From 4a86da95f953dbdaab81e4892cd43d41b20baafe Mon Sep 17 00:00:00 2001 From: Andy Melnikov Date: Sun, 9 Apr 2023 14:58:05 +0200 Subject: [PATCH 114/130] Remove trailing spaces throughout This happens often in PRs so fixing this everywhere will make many PRs mergeable as they won't include irrelevant whitespace fixes --- CONTRIBUTING.md | 2 +- README.md | 8 +-- outputs/logs/message-log-1.txt | 42 ++++++++-------- outputs/logs/message-log-2.txt | 92 +++++++++++++++++----------------- outputs/logs/message-log-3.txt | 12 ++--- scripts/ai_functions.py | 2 +- scripts/commands.py | 12 ++--- scripts/config.py | 10 ++-- scripts/data.py | 2 +- scripts/data/prompt.txt | 2 +- scripts/execute_code.py | 2 +- scripts/image_gen.py | 4 +- scripts/main.py | 14 +++--- tests/json_tests.py | 6 +-- 14 files changed, 105 insertions(+), 105 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 001d554706..09a604eba9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ git checkout -b "branch-name" 5. Add the changes to the staging area using the following command: ``` -git add . +git add . ``` 6. Commit the changes with a meaningful commit message using the following command: diff --git a/README.md b/README.md index d5d6379247..016fd6f802 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Auto-GPT is an experimental open-source application showcasing the capabilities https://user-images.githubusercontent.com/22963551/228855501-2f5777cf-755b-4407-a643-c7299e5b6419.mp4 -

πŸ’– Help Fund Auto-GPT's Development πŸ’–

+

πŸ’– Help Fund Auto-GPT's Development πŸ’–

If you can spare a coffee, you can help to cover the API costs of developing Auto-GPT and help push the boundaries of fully autonomous AI! A full day of development can easily cost as much as $20 in API costs, which for a free project is quite limiting. @@ -211,8 +211,8 @@ export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp ## πŸ’€ Continuous Mode ⚠️ Run the AI **without** user authorisation, 100% automated. -Continuous mode is not recommended. -It is potentially dangerous and may cause your AI to run forever or carry out actions you would not usually authorise. +Continuous mode is not recommended. +It is potentially dangerous and may cause your AI to run forever or carry out actions you would not usually authorise. Use at your own risk. 1. Run the `main.py` Python script in your terminal: ``` @@ -257,7 +257,7 @@ As an autonomous experiment, Auto-GPT may generate content or take actions that By using Auto-GPT, you agree to indemnify, defend, and hold harmless the developers, contributors, and any affiliated parties from and against any and all claims, damages, losses, liabilities, costs, and expenses (including reasonable attorneys' fees) arising from your use of this software or your violation of these terms. -## 🐦 Connect with Us on Twitter +## 🐦 Connect with Us on Twitter Stay up-to-date with the latest news, updates, and insights about Auto-GPT by following our Twitter accounts. Engage with the developer and the AI's own account for interesting discussions, project updates, and more. diff --git a/outputs/logs/message-log-1.txt b/outputs/logs/message-log-1.txt index de2cd1a99a..8a719016ce 100644 --- a/outputs/logs/message-log-1.txt +++ b/outputs/logs/message-log-1.txt @@ -28,15 +28,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -50,7 +50,7 @@ Gmail: entrepreneurgpt@gmail.com Twitter: @En_GPT """ ----------------------------------------------------------------------- -Entrepreneur-GTP: +Entrepreneur-GTP: { "command": { @@ -69,7 +69,7 @@ Entrepreneur-GTP: } } ----------------------------------------------------------------------- -USER: +USER: RESULT: """ 29 best online business ideas @@ -117,7 +117,7 @@ https://www.visitor-analytics.io β€Ί blog β€Ί best-online-b... 26/10/2022 β€” 10 Best Online Business Ideas for 2023 Β· 1. Artificial Intelligence Β· 2. Digital Products Β· 3. Blogging Β· 4. Graphic Design Β· 5. Web Development Β· 6. """ ----------------------------------------------------------------------- -Entrepreneur-GTP: +Entrepreneur-GTP: { "command": { @@ -167,15 +167,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -193,7 +193,7 @@ Online business ideas: 1. Clothing line, 2. Dropshipping, 3. Sell art, 4. Freela """ """ ----------------------------------------------------------------------- -Entrepreneur-GTP: +Entrepreneur-GTP: { "command": { @@ -282,15 +282,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -334,7 +334,7 @@ Entrepreneur-GTP: ----------------------------------------------------------------------- USER: """ -Top Result: +Top Result: """ Low-Cost Business Ideas: Mobile Food Shop: Mobile Food Shop is the top small business idea as people all over the world love to eat tasty and healthy food. ... @@ -403,15 +403,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", diff --git a/outputs/logs/message-log-2.txt b/outputs/logs/message-log-2.txt index cb0e59e203..a9fe8d3386 100644 --- a/outputs/logs/message-log-2.txt +++ b/outputs/logs/message-log-2.txt @@ -31,15 +31,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -143,15 +143,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -222,15 +222,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -373,15 +373,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -458,15 +458,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -515,10 +515,10 @@ Entrepreneur-GTP: } """ ----------------------------------------------------------------------- -Developer NOTE: I have interviened here to reccomend an alternative platform, since I know substack will make the process far more painless for me. +Developer NOTE: I have interviened here to reccomend an alternative platform, since I know substack will make the process far more painless for me. USER: """ -Recommendation: +Recommendation: """ Use https://substack.com instead of WordPress for simplicity. """ """ ----------------------------------------------------------------------- @@ -670,15 +670,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -758,15 +758,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -917,15 +917,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -1080,15 +1080,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -1210,10 +1210,10 @@ Quora https://www.quora.com β€Ί How-do-you-promote-a-new-fi... 6/10/2018 β€” One of the effective ways to promote blogs and articles, in general, is to run paid advertising campaigns through a good ad network that delivers targeted ... 2 answers - + Β· - -3 votes: + +3 votes: Promoting your new blog may be difficult if you’re a newbie. But trust me, you’d ... How to start a personal finance blog when I have no ... 3 answers @@ -1331,15 +1331,15 @@ These are the commands available from you: Your response must ALWAYS be in JSON format, in the following format: { - "command": + "command": { "name": "command name", - "arguments": + "arguments": { "argument name": "argument value" } - }, - "Thoughts": + }, + "Thoughts": { "text": "thought", "reasoning": "reasoning", @@ -1486,4 +1486,4 @@ USER: Entrepreneur-GTP: """ -""" \ No newline at end of file +""" diff --git a/outputs/logs/message-log-3.txt b/outputs/logs/message-log-3.txt index b528a8cf91..6e9061617b 100644 --- a/outputs/logs/message-log-3.txt +++ b/outputs/logs/message-log-3.txt @@ -134,7 +134,7 @@ Entrepreneur-GTP: ----------------------------------------------------------------------- USER: """ -Summary: +Summary: """ The article discusses 18 profitable online business ideas, including starting an e-commerce store, providing virtual assistant services, becoming an affiliate marketer, offering online training or education, and creating mobile applications. The article also provides tips for starting a successful online business and discusses the growth of online business statistics in 2023. Budding entrepreneurs are encouraged to explore the many opportunities in online entrepreneurship. """ @@ -396,7 +396,7 @@ LONG TERM MEMORY: [0] : """18 profitable online business ideas: e-commerce store, virtual assistant services, affiliate marketing, online training or education, mobile applications""", [1] : """ Chosen business idea: Blogging with affiliate marketing -""" +""" } """ ----------------------------------------------------------------------- @@ -564,7 +564,7 @@ LONG TERM MEMORY: [0] : """18 profitable online business ideas: e-commerce store, virtual assistant services, affiliate marketing, online training or education, mobile applications""", [1] : """ Chosen business idea: Blogging with affiliate marketing -""" +""" [2] : """ Blogging with affiliate marketing strategy: 1. Choose a niche, 2. Domain and hosting, 3. Design your blog, 4. Create valuable content, 5. SEO optimization, 6. Build an email list, 7. Join affiliate programs, 8. Integrate affiliate links, 9. Promote your blog, 10. Analyze and adjust """ @@ -654,7 +654,7 @@ LONG TERM MEMORY: [0] : """18 profitable online business ideas: e-commerce store, virtual assistant services, affiliate marketing, online training or education, mobile applications""", [1] : """ Chosen business idea: Blogging with affiliate marketing -""" +""" [2] : """ Blogging with affiliate marketing strategy: 1. Choose a niche, 2. Domain and hosting, 3. Design your blog, 4. Create valuable content, 5. SEO optimization, 6. Build an email list, 7. Join affiliate programs, 8. Integrate affiliate links, 9. Promote your blog, 10. Analyze and adjust """ @@ -792,7 +792,7 @@ LONG TERM MEMORY: [0] : """18 profitable online business ideas: e-commerce store, virtual assistant services, affiliate marketing, online training or education, mobile applications""", [1] : """ Chosen business idea: Blogging with affiliate marketing -""" +""" [2] : """ Blogging with affiliate marketing strategy: 1. Choose a niche, 2. Domain and hosting, 3. Design your blog, 4. Create valuable content, 5. SEO optimization, 6. Build an email list, 7. Join affiliate programs, 8. Integrate affiliate links, 9. Promote your blog, 10. Analyze and adjust """ @@ -901,7 +901,7 @@ LONG TERM MEMORY: [0] : """18 profitable online business ideas: e-commerce store, virtual assistant services, affiliate marketing, online training or education, mobile applications""", [1] : """ Chosen business idea: Blogging with affiliate marketing -""" +""" [2] : """ Blogging with affiliate marketing strategy: 1. Choose a niche, 2. Domain and hosting, 3. Design your blog, 4. Create valuable content, 5. SEO optimization, 6. Build an email list, 7. Join affiliate programs, 8. Integrate affiliate links, 9. Promote your blog, 10. Analyze and adjust """ diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index f93d7ea693..0150335caf 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -13,7 +13,7 @@ def evaluate_code(code: str) -> List[str]: description_string = """Analyzes the given code and returns a list of suggestions for improvements.""" result_string = call_ai_function(function_string, args, description_string) - + return result_string diff --git a/scripts/commands.py b/scripts/commands.py index 5e14f6cc0a..073ecc56a9 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -28,15 +28,15 @@ def get_command(response): """Parse the response and return the command name and arguments""" try: response_json = fix_and_parse_json(response) - + if "command" not in response_json: return "Error:" , "Missing 'command' object in JSON" - + command = response_json["command"] if "name" not in command: return "Error:", "Missing 'name' field in 'command' object" - + command_name = command["name"] # Use an empty dictionary if 'args' field is not present in 'command' object @@ -146,20 +146,20 @@ def google_official_search(query, num_results=8): # Initialize the Custom Search API service service = build("customsearch", "v1", developerKey=api_key) - + # Send the search query and retrieve the results result = service.cse().list(q=query, cx=custom_search_engine_id, num=num_results).execute() # Extract the search result items from the response search_results = result.get("items", []) - + # Create a list of only the URLs from the search results search_results_links = [item["link"] for item in search_results] except HttpError as e: # Handle errors in the API call error_details = json.loads(e.content.decode()) - + # Check if the error is related to an invalid or missing API key if error_details.get("error", {}).get("code") == 403 and "invalid API key" in error_details.get("error", {}).get("message", ""): return "Error: The provided Google API key is invalid or missing." diff --git a/scripts/config.py b/scripts/config.py index 03f1d5df27..764625b050 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -36,12 +36,12 @@ class Config(metaclass=Singleton): self.debug = False self.continuous_mode = False self.speak_mode = False - # TODO - make these models be self-contained, using langchain, so we can configure them once and call it good - self.fast_llm_model = os.getenv("FAST_LLM_MODEL", "gpt-3.5-turbo") + # TODO - make these models be self-contained, using langchain, so we can configure them once and call it good + self.fast_llm_model = os.getenv("FAST_LLM_MODEL", "gpt-3.5-turbo") self.smart_llm_model = os.getenv("SMART_LLM_MODEL", "gpt-4") self.fast_token_limit = int(os.getenv("FAST_TOKEN_LIMIT", 4000)) self.smart_token_limit = int(os.getenv("SMART_TOKEN_LIMIT", 8000)) - + self.openai_api_key = os.getenv("OPENAI_API_KEY") self.use_azure = False self.use_azure = os.getenv("USE_AZURE") == 'True' @@ -52,9 +52,9 @@ class Config(metaclass=Singleton): openai.api_type = "azure" openai.api_base = self.openai_api_base openai.api_version = self.openai_api_version - + self.elevenlabs_api_key = os.getenv("ELEVENLABS_API_KEY") - + self.google_api_key = os.getenv("GOOGLE_API_KEY") self.custom_search_engine_id = os.getenv("CUSTOM_SEARCH_ENGINE_ID") diff --git a/scripts/data.py b/scripts/data.py index cd41f31373..f80c2875d8 100644 --- a/scripts/data.py +++ b/scripts/data.py @@ -7,7 +7,7 @@ def load_prompt(): # get directory of this file: file_dir = Path(__file__).parent prompt_file_path = file_dir / "data" / "prompt.txt" - + # Load the prompt from data/prompt.txt with open(prompt_file_path, "r") as prompt_file: prompt = prompt_file.read() diff --git a/scripts/data/prompt.txt b/scripts/data/prompt.txt index ab281e81a0..fc68f3ae0d 100644 --- a/scripts/data/prompt.txt +++ b/scripts/data/prompt.txt @@ -35,7 +35,7 @@ RESOURCES: PERFORMANCE EVALUATION: -1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities. +1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities. 2. Constructively self-criticize your big-picture behavior constantly. 3. Reflect on past decisions and strategies to refine your approach. 4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps. diff --git a/scripts/execute_code.py b/scripts/execute_code.py index f34469dda8..a8f909116e 100644 --- a/scripts/execute_code.py +++ b/scripts/execute_code.py @@ -40,7 +40,7 @@ def execute_python_file(file): container.remove() # print(f"Execution complete. Output: {output}") - # print(f"Logs: {logs}") + # print(f"Logs: {logs}") return logs diff --git a/scripts/image_gen.py b/scripts/image_gen.py index 185ed4278b..4481696ffa 100644 --- a/scripts/image_gen.py +++ b/scripts/image_gen.py @@ -14,7 +14,7 @@ working_directory = "auto_gpt_workspace" def generate_image(prompt): filename = str(uuid.uuid4()) + ".jpg" - + # DALL-E if cfg.image_provider == 'dalle': @@ -54,4 +54,4 @@ def generate_image(prompt): return "Saved to disk:" + filename else: - return "No Image Provider Set" \ No newline at end of file + return "No Image Provider Set" diff --git a/scripts/main.py b/scripts/main.py index ecb3d51fde..ef60dc71c8 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -135,7 +135,7 @@ def load_variables(config_file="config.yaml"): if ai_name == "": ai_name = "Entrepreneur-GPT" - if not ai_role: + if not ai_role: ai_role = input(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." @@ -152,7 +152,7 @@ def load_variables(config_file="config.yaml"): ai_goals.append(ai_goal) if len(ai_goals) == 0: ai_goals = ["Increase net worth", "Grow Twitter Account", "Develop and manage multiple businesses autonomously"] - + # Save variables to yaml file config = {"ai_name": ai_name, "ai_role": ai_role, "ai_goals": ai_goals} with open(config_file, "w") as file: @@ -179,22 +179,22 @@ def construct_prompt(): Fore.GREEN, f"Would you like me to return to being {config.ai_name}?", speak_text=True) - should_continue = input(f"""Continue with the last settings? + should_continue = input(f"""Continue with the last settings? Name: {config.ai_name} Role: {config.ai_role} -Goals: {config.ai_goals} +Goals: {config.ai_goals} Continue (y/n): """) if should_continue.lower() == "n": config = AIConfig() - if not config.ai_name: + if not config.ai_name: config = prompt_user() config.save() # Get rid of this global: global ai_name ai_name = config.ai_name - + full_prompt = config.construct_full_prompt() return full_prompt @@ -257,7 +257,7 @@ def parse_arguments(): global cfg cfg.set_continuous_mode(False) cfg.set_speak_mode(False) - + parser = argparse.ArgumentParser(description='Process arguments.') parser.add_argument('--continuous', action='store_true', help='Enable Continuous Mode') parser.add_argument('--speak', action='store_true', help='Enable Speak Mode') diff --git a/tests/json_tests.py b/tests/json_tests.py index 459da610b8..fdac9c2f77 100644 --- a/tests/json_tests.py +++ b/tests/json_tests.py @@ -11,12 +11,12 @@ class TestParseJson(unittest.TestCase): json_str = '{"name": "John", "age": 30, "city": "New York"}' obj = fix_and_parse_json(json_str) self.assertEqual(obj, {"name": "John", "age": 30, "city": "New York"}) - + def test_invalid_json_minor(self): # Test that an invalid JSON string can be fixed with gpt json_str = '{"name": "John", "age": 30, "city": "New York",}' self.assertEqual(fix_and_parse_json(json_str, try_to_fix_with_gpt=False), {"name": "John", "age": 30, "city": "New York"}) - + def test_invalid_json_major_with_gpt(self): # Test that an invalid JSON string raises an error when try_to_fix_with_gpt is False json_str = 'BEGIN: "name": "John" - "age": 30 - "city": "New York" :END' @@ -112,4 +112,4 @@ class TestParseJson(unittest.TestCase): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() From 186235f488b970f457a026967a416dbca82a9ac2 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Mon, 10 Apr 2023 13:54:27 +0100 Subject: [PATCH 115/130] Fixes missing cfg in main.py --- scripts/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/main.py b/scripts/main.py index 844c2375d2..c4fa2a20e6 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -17,6 +17,8 @@ import traceback import yaml import argparse +cfg = Config() + def check_openai_api_key(): """Check if the OpenAI API key is set in config.py or as an environment variable.""" if not cfg.openai_api_key: From d304740f9d727b0561f76bcaa649a2800be98ba1 Mon Sep 17 00:00:00 2001 From: Phoebe Bright Date: Mon, 10 Apr 2023 14:17:56 +0100 Subject: [PATCH 116/130] Update .env.template Remove quotes for model variables that can lead to the model not being recognised and error num_tokens_from_messages() is not implemented for model --- .env.template | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.template b/.env.template index 525cd61c5f..8185cc66af 100644 --- a/.env.template +++ b/.env.template @@ -2,8 +2,8 @@ PINECONE_API_KEY=your-pinecone-api-key PINECONE_ENV=your-pinecone-region OPENAI_API_KEY=your-openai-api-key ELEVENLABS_API_KEY=your-elevenlabs-api-key -SMART_LLM_MODEL="gpt-4" -FAST_LLM_MODEL="gpt-3.5-turbo" +SMART_LLM_MODEL=gpt-4 +FAST_LLM_MODEL=gpt-3.5-turbo GOOGLE_API_KEY= CUSTOM_SEARCH_ENGINE_ID= USE_AZURE=False @@ -11,4 +11,4 @@ OPENAI_API_BASE=your-base-url-for-azure OPENAI_API_VERSION=api-version-for-azure OPENAI_DEPLOYMENT_ID=deployment-id-for-azure IMAGE_PROVIDER=dalle -HUGGINGFACE_API_TOKEN= \ No newline at end of file +HUGGINGFACE_API_TOKEN= From fa8461be9de417a5b84322de116f02157a235a7e Mon Sep 17 00:00:00 2001 From: onekum <55006697+onekum@users.noreply.github.com> Date: Mon, 10 Apr 2023 09:21:43 -0400 Subject: [PATCH 117/130] Restrict browse from accessing local files --- scripts/browse.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/browse.py b/scripts/browse.py index c15214e7dd..4a73d92320 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -11,6 +11,10 @@ def scrape_text(url): if not url.startswith('http'): return "Error: Invalid URL" + # Restrict access to local files + if url.startswith('file://') or url.startswith('file://localhost'): + return "Error: Access to local files is restricted" + try: response = requests.get(url, headers=cfg.user_agent_header) except requests.exceptions.RequestException as e: @@ -126,4 +130,4 @@ def summarize_text(text, question): max_tokens=300, ) - return final_summary \ No newline at end of file + return final_summary From 026361aa3a634da6234776d1d49fa2a0fa029367 Mon Sep 17 00:00:00 2001 From: cryptidv Date: Mon, 10 Apr 2023 14:22:11 +0100 Subject: [PATCH 118/130] Changed occurances of 'cfg.debug_mode' to 'cfg.debug' --- scripts/chat.py | 6 +++--- scripts/json_parser.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/chat.py b/scripts/chat.py index 4a7056d2b1..c00e4d4a60 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -64,14 +64,14 @@ def chat_with_ai( model = cfg.fast_llm_model # TODO: Change model from hardcode to argument # Reserve 1000 tokens for the response - if cfg.debug_mode: + if cfg.debug: print(f"Token limit: {token_limit}") send_token_limit = token_limit - 1000 relevant_memory = permanent_memory.get_relevant(str(full_message_history[-5:]), 10) - if cfg.debug_mode: + if cfg.debug: print('Memory Stats: ', permanent_memory.get_stats()) next_message_to_add_index, current_tokens_used, insertion_index, current_context = generate_context( @@ -110,7 +110,7 @@ def chat_with_ai( # assert tokens_remaining >= 0, "Tokens remaining is negative. This should never happen, please submit a bug report at https://www.github.com/Torantulino/Auto-GPT" # Debug print the current context - if cfg.debug_mode: + if cfg.debug: print(f"Token limit: {token_limit}") print(f"Send Token Count: {current_tokens_used}") print(f"Tokens remaining for response: {tokens_remaining}") diff --git a/scripts/json_parser.py b/scripts/json_parser.py index 4165a1d23b..1fd6824408 100644 --- a/scripts/json_parser.py +++ b/scripts/json_parser.py @@ -91,7 +91,7 @@ def fix_json(json_str: str, schema: str) -> str: result_string = call_ai_function( function_string, args, description_string, model=cfg.fast_llm_model ) - if cfg.debug_mode: + if cfg.debug: print("------------ JSON FIX ATTEMPT ---------------") print(f"Original JSON: {json_str}") print("-----------") From 37b4473137d9fc5601c1a11fdc1859b65bfb75a2 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Mon, 10 Apr 2023 14:23:20 +0100 Subject: [PATCH 119/130] Fixes extra arg given in chat-with-ai call in main --- scripts/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index c4fa2a20e6..3dfcaa157e 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -320,7 +320,7 @@ while True: user_input, full_message_history, memory, - cfg.fast_token_limit, cfg.debug) # TODO: This hardcodes the model to use GPT3.5. Make this an argument + cfg.fast_token_limit) # TODO: This hardcodes the model to use GPT3.5. Make this an argument # Print Assistant thoughts print_assistant_thoughts(assistant_reply) From c300276d6d816e44c4d0be0e0e397191d3200826 Mon Sep 17 00:00:00 2001 From: Toran Bruce Richards Date: Mon, 10 Apr 2023 14:32:13 +0100 Subject: [PATCH 120/130] Change debug_mode to debug in chat.py. --- scripts/chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/chat.py b/scripts/chat.py index 4a7056d2b1..67cfe52a68 100644 --- a/scripts/chat.py +++ b/scripts/chat.py @@ -64,7 +64,7 @@ def chat_with_ai( model = cfg.fast_llm_model # TODO: Change model from hardcode to argument # Reserve 1000 tokens for the response - if cfg.debug_mode: + if cfg.debug: print(f"Token limit: {token_limit}") send_token_limit = token_limit - 1000 From 527e084f39c7c05406a42c9b83ebd87c3194b231 Mon Sep 17 00:00:00 2001 From: yoshikouki Date: Mon, 10 Apr 2023 22:57:41 +0900 Subject: [PATCH 121/130] Remove unnecessary assignments --- scripts/commands.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/commands.py b/scripts/commands.py index 073ecc56a9..ce5d04ff48 100644 --- a/scripts/commands.py +++ b/scripts/commands.py @@ -42,9 +42,6 @@ def get_command(response): # Use an empty dictionary if 'args' field is not present in 'command' object arguments = command.get("args", {}) - if not arguments: - arguments = {} - return command_name, arguments except json.decoder.JSONDecodeError: return "Error:", "Invalid JSON" From cd07115a39fa30f28c4f85ad4c776a2cfb817260 Mon Sep 17 00:00:00 2001 From: Sma Das Date: Mon, 10 Apr 2023 11:29:15 -0400 Subject: [PATCH 122/130] Removed trailing whitespace --- scripts/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/main.py b/scripts/main.py index bd1c088d20..71e72cd4c3 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -138,7 +138,7 @@ def load_variables(config_file="config.yaml"): if ai_name == "": ai_name = "Entrepreneur-GPT" - if not ai_role: + if not ai_role: ai_role = utils.clean_input(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." @@ -182,7 +182,7 @@ def construct_prompt(): Fore.GREEN, f"Would you like me to return to being {config.ai_name}?", speak_text=True) - should_continue = utils.clean_input(f"""Continue with the last settings? + should_continue = utils.clean_input(f"""Continue with the last settings? Name: {config.ai_name} Role: {config.ai_role} Goals: {config.ai_goals} @@ -282,7 +282,7 @@ def parse_arguments(): if args.debug: print_to_console("Debug Mode: ", Fore.GREEN, "ENABLED") - cfg.set_debug_mode(True) + cfg.set_debug_mode(True) if args.gpt3only: print_to_console("GPT3.5 Only Mode: ", Fore.GREEN, "ENABLED") From 5c1c3f9c7e9be6cfec4cca255123f81756ba0d10 Mon Sep 17 00:00:00 2001 From: Sma Das Date: Mon, 10 Apr 2023 11:31:43 -0400 Subject: [PATCH 123/130] Added exit message on `KeyboardInterrupt` --- scripts/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/utils.py b/scripts/utils.py index bab9ac9684..5039796fbd 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -2,5 +2,7 @@ def clean_input(prompt: str=''): try: return input(prompt) except KeyboardInterrupt: + print("You interrupted Auto-GPT") + print("Quitting...") exit(0) From b60c7518b0640ae224f989a2cd964b37e0ca90bf Mon Sep 17 00:00:00 2001 From: onekum <55006697+onekum@users.noreply.github.com> Date: Mon, 10 Apr 2023 12:10:28 -0400 Subject: [PATCH 124/130] Rework local file address blocks add `def check_local_file_access`, which defines and checks for local file address prefixes; use it to restrict access --- scripts/browse.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/browse.py b/scripts/browse.py index 4a73d92320..09f376a70a 100644 --- a/scripts/browse.py +++ b/scripts/browse.py @@ -5,6 +5,11 @@ from llm_utils import create_chat_completion cfg = Config() +# Define and check for local file address prefixes +def check_local_file_access(url): + local_prefixes = ['file:///', 'file://localhost', 'http://localhost', 'https://localhost'] + return any(url.startswith(prefix) for prefix in local_prefixes) + def scrape_text(url): """Scrape text from a webpage""" # Most basic check if the URL is valid: @@ -12,7 +17,7 @@ def scrape_text(url): return "Error: Invalid URL" # Restrict access to local files - if url.startswith('file://') or url.startswith('file://localhost'): + if check_local_file_access(url): return "Error: Access to local files is restricted" try: From c9701a330294fc9eba1de3669ceae313fc735ea7 Mon Sep 17 00:00:00 2001 From: Andy Melnikov Date: Mon, 10 Apr 2023 18:17:11 +0200 Subject: [PATCH 125/130] Remove trailing whitespace throughout --- scripts/ai_config.py | 10 +++++----- scripts/ai_functions.py | 2 +- scripts/config.py | 2 +- scripts/main.py | 2 +- tests/test_browse_scrape_text.py | 12 ++++++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/ai_config.py b/scripts/ai_config.py index 8fabf3dcee..1d5832c182 100644 --- a/scripts/ai_config.py +++ b/scripts/ai_config.py @@ -34,7 +34,7 @@ class AIConfig: @classmethod def load(cls: object, config_file: str=SAVE_FILE) -> object: """ - Returns class object with parameters (ai_name, ai_role, ai_goals) loaded from yaml file if yaml file exists, + Returns class object with parameters (ai_name, ai_role, ai_goals) loaded from yaml file if yaml file exists, else returns class with no parameters. Parameters: @@ -42,7 +42,7 @@ class AIConfig: config_file (int): The path to the config yaml file. DEFAULT: "../ai_settings.yaml" Returns: - cls (object): A instance of given cls object + cls (object): A instance of given cls object """ try: @@ -61,11 +61,11 @@ class AIConfig: """ Saves the class parameters to the specified file yaml file path as a yaml file. - Parameters: + Parameters: config_file(str): The path to the config yaml file. DEFAULT: "../ai_settings.yaml" Returns: - None + None """ config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals} @@ -76,7 +76,7 @@ class AIConfig: """ Returns a prompt to the user with the class information in an organized fashion. - Parameters: + Parameters: None Returns: diff --git a/scripts/ai_functions.py b/scripts/ai_functions.py index dc774b37cb..8ad77441dc 100644 --- a/scripts/ai_functions.py +++ b/scripts/ai_functions.py @@ -27,7 +27,7 @@ def evaluate_code(code: str) -> List[str]: def improve_code(suggestions: List[str], code: str) -> str: """ - A function that takes in code and suggestions and returns a response from create chat completion api call. + A function that takes in code and suggestions and returns a response from create chat completion api call. Parameters: suggestions (List): A list of suggestions around what needs to be improved. diff --git a/scripts/config.py b/scripts/config.py index 1eb74b2bc7..06af2b0586 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -37,7 +37,7 @@ class Config(metaclass=Singleton): self.continuous_mode = False self.speak_mode = False - self.fast_llm_model = os.getenv("FAST_LLM_MODEL", "gpt-3.5-turbo") + self.fast_llm_model = os.getenv("FAST_LLM_MODEL", "gpt-3.5-turbo") self.smart_llm_model = os.getenv("SMART_LLM_MODEL", "gpt-4") self.fast_token_limit = int(os.getenv("FAST_TOKEN_LIMIT", 4000)) self.smart_token_limit = int(os.getenv("SMART_TOKEN_LIMIT", 8000)) diff --git a/scripts/main.py b/scripts/main.py index 3dfcaa157e..4481b1ff29 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -281,7 +281,7 @@ def parse_arguments(): if args.debug: print_to_console("Debug Mode: ", Fore.GREEN, "ENABLED") - cfg.set_debug_mode(True) + cfg.set_debug_mode(True) if args.gpt3only: print_to_console("GPT3.5 Only Mode: ", Fore.GREEN, "ENABLED") diff --git a/tests/test_browse_scrape_text.py b/tests/test_browse_scrape_text.py index 27ebc0f693..bfc3b4251d 100644 --- a/tests/test_browse_scrape_text.py +++ b/tests/test_browse_scrape_text.py @@ -37,7 +37,7 @@ Additional aspects: class TestScrapeText: - # Tests that scrape_text() returns the expected text when given a valid URL. + # Tests that scrape_text() returns the expected text when given a valid URL. def test_scrape_text_with_valid_url(self, mocker): # Mock the requests.get() method to return a response with expected text expected_text = "This is some sample text" @@ -50,7 +50,7 @@ class TestScrapeText: url = "http://www.example.com" assert scrape_text(url) == expected_text - # Tests that the function returns an error message when an invalid or unreachable url is provided. + # Tests that the function returns an error message when an invalid or unreachable url is provided. def test_invalid_url(self, mocker): # Mock the requests.get() method to raise an exception mocker.patch("requests.get", side_effect=requests.exceptions.RequestException) @@ -60,7 +60,7 @@ class TestScrapeText: error_message = scrape_text(url) assert "Error:" in error_message - # Tests that the function returns an empty string when the html page contains no text to be scraped. + # Tests that the function returns an empty string when the html page contains no text to be scraped. def test_no_text(self, mocker): # Mock the requests.get() method to return a response with no text mock_response = mocker.Mock() @@ -72,7 +72,7 @@ class TestScrapeText: url = "http://www.example.com" assert scrape_text(url) == "" - # Tests that the function returns an error message when the response status code is an http error (>=400). + # Tests that the function returns an error message when the response status code is an http error (>=400). def test_http_error(self, mocker): # Mock the requests.get() method to return a response with a 404 status code mocker.patch('requests.get', return_value=mocker.Mock(status_code=404)) @@ -83,7 +83,7 @@ class TestScrapeText: # Check that the function returns an error message assert result == "Error: HTTP 404 error" - # Tests that scrape_text() properly handles HTML tags. + # Tests that scrape_text() properly handles HTML tags. def test_scrape_text_with_html_tags(self, mocker): # Create a mock response object with HTML containing tags html = "

This is bold text.

" @@ -96,4 +96,4 @@ class TestScrapeText: result = scrape_text("https://www.example.com") # Check that the function properly handles HTML tags - assert result == "This is bold text." \ No newline at end of file + assert result == "This is bold text." From a5b48790fc8266de45f52c393799f9100c418909 Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Mon, 10 Apr 2023 19:37:32 +0100 Subject: [PATCH 126/130] Delete ai_settings.yaml --- ai_settings.yaml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ai_settings.yaml diff --git a/ai_settings.yaml b/ai_settings.yaml deleted file mode 100644 index 1271a6e211..0000000000 --- a/ai_settings.yaml +++ /dev/null @@ -1,6 +0,0 @@ -ai_goals: -- Increase net worth -- Grow Twitter Account -- Develop and manage multiple businesses autonomously -ai_name: Test -ai_role: Test1 From 818920e22faa82cc91821102caef5312e0ea985e Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Mon, 10 Apr 2023 19:48:42 +0100 Subject: [PATCH 127/130] restore default ai_settings --- ai_settings.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ai_settings.yaml diff --git a/ai_settings.yaml b/ai_settings.yaml new file mode 100644 index 0000000000..b37ba849f9 --- /dev/null +++ b/ai_settings.yaml @@ -0,0 +1,7 @@ +ai_goals: +- Increase net worth. +- Develop and manage multiple businesses autonomously. +- Play to your strengths as a Large Language Model. +ai_name: Entrepreneur-GPT +ai_role: an AI designed to autonomously develop and run businesses with the sole goal + of increasing your net worth. From c6bdb0f0baf14d84b349330d24d692470e0ff35d Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Mon, 10 Apr 2023 19:50:13 +0100 Subject: [PATCH 128/130] remove trailing newline --- scripts/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index 469a71c180..0ba652939c 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -415,4 +415,4 @@ while True: full_message_history.append( chat.create_chat_message( "system", "Unable to execute command")) - print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") + print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") \ No newline at end of file From f69e46208e213090cbbf6c329e67466575975d62 Mon Sep 17 00:00:00 2001 From: Richard Beales Date: Mon, 10 Apr 2023 19:50:56 +0100 Subject: [PATCH 129/130] remove trailing newline --- scripts/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/main.py b/scripts/main.py index 0ba652939c..ca75eb240d 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -415,4 +415,5 @@ while True: full_message_history.append( chat.create_chat_message( "system", "Unable to execute command")) - print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") \ No newline at end of file + print_to_console("SYSTEM: ", Fore.YELLOW, "Unable to execute command") + From 3146d97a89e59502bfa05a95c64338b279fcfbe8 Mon Sep 17 00:00:00 2001 From: amit9021 Date: Mon, 10 Apr 2023 23:46:08 +0300 Subject: [PATCH 130/130] README pinecone link added --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 118131e463..dae392c3ed 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Your support is greatly appreciated ## πŸ“‹ Requirements - [Python 3.8 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows) - OpenAI API key -- PINECONE API key +- [PINECONE API key](https://www.pinecone.io/) Optional: - ElevenLabs Key (If you want the AI to speak)