Merge branch 'master' into security-and-robustness-improvements

pull/215/head
Slava Kurilyak (slavakurilyak.eth) 2023-04-11 09:32:51 -06:00 committed by GitHub
commit e9e4f11b4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 117 additions and 81 deletions

View File

@ -2,13 +2,14 @@ PINECONE_API_KEY=your-pinecone-api-key
PINECONE_ENV=your-pinecone-region PINECONE_ENV=your-pinecone-region
OPENAI_API_KEY=your-openai-api-key OPENAI_API_KEY=your-openai-api-key
ELEVENLABS_API_KEY=your-elevenlabs-api-key ELEVENLABS_API_KEY=your-elevenlabs-api-key
SMART_LLM_MODEL="gpt-4" SMART_LLM_MODEL=gpt-4
FAST_LLM_MODEL="gpt-3.5-turbo" FAST_LLM_MODEL=gpt-3.5-turbo
GOOGLE_API_KEY= GOOGLE_API_KEY=
CUSTOM_SEARCH_ENGINE_ID= CUSTOM_SEARCH_ENGINE_ID=
USE_AZURE=False USE_AZURE=False
OPENAI_API_BASE=your-base-url-for-azure OPENAI_AZURE_API_BASE=your-base-url-for-azure
OPENAI_API_VERSION=api-version-for-azure OPENAI_AZURE_API_VERSION=api-version-for-azure
OPENAI_DEPLOYMENT_ID=deployment-id-for-azure OPENAI_AZURE_DEPLOYMENT_ID=deployment-id-for-azure
IMAGE_PROVIDER=dalle IMAGE_PROVIDER=dalle
HUGGINGFACE_API_TOKEN= HUGGINGFACE_API_TOKEN=
USE_MAC_OS_TTS=False

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ outputs/*
ai_settings.yaml ai_settings.yaml
.vscode .vscode
auto-gpt.json auto-gpt.json
log.txt

View File

@ -59,7 +59,7 @@ Your support is greatly appreciated
## 📋 Requirements ## 📋 Requirements
- [Python 3.8 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 - OpenAI API key
- PINECONE API key - [PINECONE API key](https://www.pinecone.io/)
Optional: Optional:
- ElevenLabs Key (If you want the AI to speak) - ElevenLabs Key (If you want the AI to speak)
@ -92,8 +92,8 @@ 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. 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 OpenAI API key from: https://platform.openai.com/account/api-keys.
- Obtain your ElevenLabs API key from: https://beta.elevenlabs.io. You can view your xi-api-key using the "Profile" tab on the website. - 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 ## 🔧 Usage
@ -179,8 +179,7 @@ MEMORY_INDEX=whatever
## 🌲 Pinecone API Key Setup ## 🌲 Pinecone API Key Setup
Pinecone enable a vector based memory so a vast memory can be stored and only relevant memories 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.
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. 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. 2. Choose the `Starter` plan to avoid being charged.

View File

@ -92,4 +92,3 @@ class AIConfig:
full_prompt += f"\n\n{data.load_prompt()}" full_prompt += f"\n\n{data.load_prompt()}"
return full_prompt return full_prompt

View File

@ -27,12 +27,21 @@ def make_request(url, timeout=10):
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return "Error: " + str(e) return "Error: " + str(e)
# 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): def scrape_text(url):
"""Scrape text from a webpage""" """Scrape text from a webpage"""
# Basic check if the URL is valid # Basic check if the URL is valid
if not url.startswith('http'): if not url.startswith('http'):
return "Error: Invalid URL" return "Error: Invalid URL"
# Restrict access to local files
if check_local_file_access(url):
return "Error: Access to local files is restricted"
# Validate the input URL # Validate the input URL
if not is_valid_url(url): if not is_valid_url(url):
# Sanitize the input URL # Sanitize the input URL

View File

@ -42,9 +42,6 @@ def get_command(response):
# Use an empty dictionary if 'args' field is not present in 'command' object # Use an empty dictionary if 'args' field is not present in 'command' object
arguments = command.get("args", {}) arguments = command.get("args", {})
if not arguments:
arguments = {}
return command_name, arguments return command_name, arguments
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
return "Error:", "Invalid JSON" return "Error:", "Invalid JSON"

View File

@ -33,7 +33,7 @@ class Config(metaclass=Singleton):
def __init__(self): def __init__(self):
"""Initialize the Config class""" """Initialize the Config class"""
self.debug = False self.debug_mode = False
self.continuous_mode = False self.continuous_mode = False
self.speak_mode = False self.speak_mode = False
@ -46,15 +46,18 @@ class Config(metaclass=Singleton):
self.use_azure = False self.use_azure = False
self.use_azure = os.getenv("USE_AZURE") == 'True' self.use_azure = os.getenv("USE_AZURE") == 'True'
if self.use_azure: if self.use_azure:
self.openai_api_base = os.getenv("OPENAI_API_BASE") self.openai_api_base = os.getenv("OPENAI_AZURE_API_BASE")
self.openai_api_version = os.getenv("OPENAI_API_VERSION") self.openai_api_version = os.getenv("OPENAI_AZURE_API_VERSION")
self.openai_deployment_id = os.getenv("OPENAI_DEPLOYMENT_ID") self.openai_deployment_id = os.getenv("OPENAI_AZURE_DEPLOYMENT_ID")
openai.api_type = "azure" openai.api_type = "azure"
openai.api_base = self.openai_api_base openai.api_base = self.openai_api_base
openai.api_version = self.openai_api_version openai.api_version = self.openai_api_version
self.elevenlabs_api_key = os.getenv("ELEVENLABS_API_KEY") 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") self.google_api_key = os.getenv("GOOGLE_API_KEY")
self.custom_search_engine_id = os.getenv("CUSTOM_SEARCH_ENGINE_ID") self.custom_search_engine_id = os.getenv("CUSTOM_SEARCH_ENGINE_ID")
@ -86,9 +89,6 @@ class Config(metaclass=Singleton):
"""Set the speak mode value.""" """Set the speak mode value."""
self.speak_mode = value self.speak_mode = value
def set_debug_mode(self, value: bool):
self.debug_mode = value
def set_fast_llm_model(self, value: str): def set_fast_llm_model(self, value: str):
"""Set the fast LLM model value.""" """Set the fast LLM model value."""
self.fast_llm_model = value self.fast_llm_model = value
@ -131,4 +131,4 @@ class Config(metaclass=Singleton):
def set_debug_mode(self, value: bool): def set_debug_mode(self, value: bool):
"""Set the debug mode value.""" """Set the debug mode value."""
self.debug = value self.debug_mode = value

View File

@ -24,7 +24,7 @@ def read_file(filename):
"""Read a file and return the contents""" """Read a file and return the contents"""
try: try:
filepath = safe_join(working_directory, filename) filepath = safe_join(working_directory, filename)
with open(filepath, "r") as f: with open(filepath, "r", encoding='utf-8') as f:
content = f.read() content = f.read()
return content return content
except Exception as e: except Exception as e:

View File

@ -76,7 +76,7 @@ def balance_braces(json_string: str) -> str:
json.loads(json_string) json.loads(json_string)
return json_string return json_string
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
raise e pass
def fix_invalid_escape(json_str: str, error_message: str) -> str: def fix_invalid_escape(json_str: str, error_message: str) -> str:
@ -88,7 +88,7 @@ def fix_invalid_escape(json_str: str, error_message: str) -> str:
json.loads(json_str) json.loads(json_str)
return json_str return json_str
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
if cfg.debug: if cfg.debug_mode:
print('json loads error - fix invalid escape', e) print('json loads error - fix invalid escape', e)
error_message = str(e) error_message = str(e)
return json_str return json_str
@ -103,12 +103,12 @@ def correct_json(json_str: str) -> str:
""" """
try: try:
if cfg.debug: if cfg.debug_mode:
print("json", json_str) print("json", json_str)
json.loads(json_str) json.loads(json_str)
return json_str return json_str
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
if cfg.debug: if cfg.debug_mode:
print('json loads error', e) print('json loads error', e)
error_message = str(e) error_message = str(e)
if error_message.startswith('Invalid \\escape'): if error_message.startswith('Invalid \\escape'):
@ -119,7 +119,7 @@ def correct_json(json_str: str) -> str:
json.loads(json_str) json.loads(json_str)
return json_str return json_str
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
if cfg.debug: if cfg.debug_mode:
print('json loads error - add quotes', e) print('json loads error - add quotes', e)
error_message = str(e) error_message = str(e)
if balanced_str := balance_braces(json_str): if balanced_str := balance_braces(json_str):

View File

@ -1,6 +1,7 @@
import json import json
import random import random
import commands as cmd import commands as cmd
import utils
from memory import get_memory from memory import get_memory
import data import data
import chat import chat
@ -8,17 +9,24 @@ from colorama import Fore, Style
from spinner import Spinner from spinner import Spinner
import time import time
import speak import speak
from enum import Enum, auto
import sys
from config import Config from config import Config
from json_parser import fix_and_parse_json from json_parser import fix_and_parse_json
from ai_config import AIConfig from ai_config import AIConfig
import traceback import traceback
import yaml import yaml
import argparse import argparse
import logging
cfg = Config() cfg = Config()
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 check_openai_api_key(): def check_openai_api_key():
"""Check if the OpenAI API key is set in config.py or as an environment variable.""" """Check if the OpenAI API key is set in config.py or as an environment variable."""
if not cfg.openai_api_key: if not cfg.openai_api_key:
@ -29,7 +37,6 @@ def check_openai_api_key():
print("You can get your key from https://beta.openai.com/account/api-keys") print("You can get your key from https://beta.openai.com/account/api-keys")
exit(1) exit(1)
def print_to_console( def print_to_console(
title, title,
title_color, title_color,
@ -39,10 +46,12 @@ def print_to_console(
max_typing_speed=0.01): max_typing_speed=0.01):
"""Prints text to the console with a typing effect""" """Prints text to the console with a typing effect"""
global cfg global cfg
global logger
if speak_text and cfg.speak_mode: if speak_text and cfg.speak_mode:
speak.say_text(f"{title}. {content}") speak.say_text(f"{title}. {content}")
print(title_color + title + " " + Style.RESET_ALL, end="") print(title_color + title + " " + Style.RESET_ALL, end="")
if content: if content:
logger.info(title + ': ' + content)
if isinstance(content, list): if isinstance(content, list):
content = " ".join(content) content = " ".join(content)
words = content.split() words = content.split()
@ -133,12 +142,12 @@ def load_variables(config_file="config.yaml"):
# Prompt the user for input if config file is missing or empty values # Prompt the user for input if config file is missing or empty values
if not ai_name: if not ai_name:
ai_name = input("Name your AI: ") ai_name = utils.clean_input("Name your AI: ")
if ai_name == "": if ai_name == "":
ai_name = "Entrepreneur-GPT" ai_name = "Entrepreneur-GPT"
if not ai_role: if not ai_role:
ai_role = input(f"{ai_name} is: ") ai_role = utils.clean_input(f"{ai_name} is: ")
if ai_role == "": if ai_role == "":
ai_role = "an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth." ai_role = "an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth."
@ -148,7 +157,7 @@ def load_variables(config_file="config.yaml"):
print("Enter nothing to load defaults, enter nothing when finished.") print("Enter nothing to load defaults, enter nothing when finished.")
ai_goals = [] ai_goals = []
for i in range(5): for i in range(5):
ai_goal = input(f"Goal {i+1}: ") ai_goal = utils.clean_input(f"Goal {i+1}: ")
if ai_goal == "": if ai_goal == "":
break break
ai_goals.append(ai_goal) ai_goals.append(ai_goal)
@ -161,7 +170,7 @@ def load_variables(config_file="config.yaml"):
documents = yaml.dump(config, file) documents = yaml.dump(config, file)
prompt = data.load_prompt() prompt = data.load_prompt()
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.""" prompt_start = """Your decisions must always be made independently without seeking user assistance. Play to your strengths as a LLM and pursue simple strategies with no legal complications."""
# Construct full prompt # Construct full prompt
full_prompt = f"You are {ai_name}, {ai_role}\n{prompt_start}\n\nGOALS:\n\n" full_prompt = f"You are {ai_name}, {ai_role}\n{prompt_start}\n\nGOALS:\n\n"
@ -181,7 +190,7 @@ def construct_prompt():
Fore.GREEN, Fore.GREEN,
f"Would you like me to return to being {config.ai_name}?", f"Would you like me to return to being {config.ai_name}?",
speak_text=True) 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} Name: {config.ai_name}
Role: {config.ai_role} Role: {config.ai_role}
Goals: {config.ai_goals} Goals: {config.ai_goals}
@ -216,7 +225,7 @@ def prompt_user():
"Name your AI: ", "Name your AI: ",
Fore.GREEN, Fore.GREEN,
"For example, 'Entrepreneur-GPT'") "For example, 'Entrepreneur-GPT'")
ai_name = input("AI Name: ") ai_name = utils.clean_input("AI Name: ")
if ai_name == "": if ai_name == "":
ai_name = "Entrepreneur-GPT" ai_name = "Entrepreneur-GPT"
@ -231,7 +240,7 @@ def prompt_user():
"Describe your AI's role: ", "Describe your AI's role: ",
Fore.GREEN, Fore.GREEN,
"For example, 'an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth.'") "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 == "": if ai_role == "":
ai_role = "an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth." ai_role = "an AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth."
@ -243,7 +252,7 @@ def prompt_user():
print("Enter nothing to load defaults, enter nothing when finished.", flush=True) print("Enter nothing to load defaults, enter nothing when finished.", flush=True)
ai_goals = [] ai_goals = []
for i in range(5): 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 == "": if ai_goal == "":
break break
ai_goals.append(ai_goal) ai_goals.append(ai_goal)
@ -279,22 +288,16 @@ def parse_arguments():
print_to_console("Speak Mode: ", Fore.GREEN, "ENABLED") print_to_console("Speak Mode: ", Fore.GREEN, "ENABLED")
cfg.set_speak_mode(True) cfg.set_speak_mode(True)
if args.debug:
print_to_console("Debug Mode: ", Fore.GREEN, "ENABLED")
cfg.set_debug_mode(True)
if args.gpt3only: if args.gpt3only:
print_to_console("GPT3.5 Only Mode: ", Fore.GREEN, "ENABLED") print_to_console("GPT3.5 Only Mode: ", Fore.GREEN, "ENABLED")
cfg.set_smart_llm_model(cfg.fast_llm_model) 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 # TODO: fill in llm values here
check_openai_api_key() check_openai_api_key()
cfg = Config() cfg = Config()
logger = configure_logging()
parse_arguments() parse_arguments()
ai_name = "" ai_name = ""
prompt = construct_prompt() prompt = construct_prompt()
@ -344,7 +347,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}...", 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) flush=True)
while 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": if console_input.lower() == "y":
user_input = "GENERATE NEXT COMMAND JSON" user_input = "GENERATE NEXT COMMAND JSON"
break break
@ -405,4 +408,3 @@ while True:
chat.create_chat_message( chat.create_chat_message(
"system", "Unable to execute command")) "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")

View File

@ -4,6 +4,8 @@ import requests
from config import Config from config import Config
cfg = Config() cfg = Config()
import gtts import gtts
import threading
from threading import Lock, Semaphore
# TODO: Nicer names for these ids # TODO: Nicer names for these ids
@ -14,6 +16,9 @@ tts_headers = {
"xi-api-key": cfg.elevenlabs_api_key "xi-api-key": cfg.elevenlabs_api_key
} }
mutex_lock = Lock() # Ensure only one sound is played at a time
queue_semaphore = Semaphore(1) # The amount of sounds to queue before blocking the main thread
def eleven_labs_speech(text, voice_index=0): def eleven_labs_speech(text, voice_index=0):
"""Speak text using elevenlabs.io's API""" """Speak text using elevenlabs.io's API"""
tts_url = "https://api.elevenlabs.io/v1/text-to-speech/{voice_id}".format( tts_url = "https://api.elevenlabs.io/v1/text-to-speech/{voice_id}".format(
@ -23,9 +28,10 @@ def eleven_labs_speech(text, voice_index=0):
tts_url, headers=tts_headers, json=formatted_message) tts_url, headers=tts_headers, json=formatted_message)
if response.status_code == 200: if response.status_code == 200:
with mutex_lock:
with open("speech.mpeg", "wb") as f: with open("speech.mpeg", "wb") as f:
f.write(response.content) f.write(response.content)
playsound("speech.mpeg") playsound("speech.mpeg", True)
os.remove("speech.mpeg") os.remove("speech.mpeg")
return True return True
else: else:
@ -35,15 +41,29 @@ def eleven_labs_speech(text, voice_index=0):
def gtts_speech(text): def gtts_speech(text):
tts = gtts.gTTS(text) tts = gtts.gTTS(text)
with mutex_lock:
tts.save("speech.mp3") tts.save("speech.mp3")
playsound("speech.mp3") playsound("speech.mp3", True)
os.remove("speech.mp3") os.remove("speech.mp3")
def macos_tts_speech(text):
os.system(f'say "{text}"')
def say_text(text, voice_index=0): def say_text(text, voice_index=0):
def speak():
if not cfg.elevenlabs_api_key: if not cfg.elevenlabs_api_key:
if cfg.use_mac_os_tts == 'True':
macos_tts_speech(text)
else:
gtts_speech(text) gtts_speech(text)
else: else:
success = eleven_labs_speech(text, voice_index) success = eleven_labs_speech(text, voice_index)
if not success: if not success:
gtts_speech(text) gtts_speech(text)
queue_semaphore.release()
queue_semaphore.acquire(True)
thread = threading.Thread(target=speak)
thread.start()

8
scripts/utils.py Normal file
View File

@ -0,0 +1,8 @@
def clean_input(prompt: str=''):
try:
return input(prompt)
except KeyboardInterrupt:
print("You interrupted Auto-GPT")
print("Quitting...")
exit(0)