Merge branch 'master' into add_ingest_documents_script

This commit is contained in:
Maiko Bossuyt
2023-04-14 18:12:23 +02:00
committed by GitHub
13 changed files with 274 additions and 178 deletions

23
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
ARG VARIANT=3-bullseye
FROM python:3.8
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# Remove imagemagick due to https://security-tracker.debian.org/tracker/CVE-2019-10131
&& apt-get purge -y imagemagick imagemagick-6-common
# Temporary: Upgrade python packages due to https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-40897
# They are installed by the base image (python) which does not have the patch.
RUN python3 -m pip install --upgrade setuptools
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
# COPY requirements.txt /tmp/pip-tmp/
# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
# && rm -rf /tmp/pip-tmp
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

View File

@@ -0,0 +1,39 @@
{
"build": {
"dockerfile": "./Dockerfile",
"context": "."
},
"features": {
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": "true",
"username": "vscode",
"userUid": "1000",
"userGid": "1000",
"upgradePackages": "true"
},
"ghcr.io/devcontainers/features/python:1": "none",
"ghcr.io/devcontainers/features/node:1": "none",
"ghcr.io/devcontainers/features/git:1": {
"version": "latest",
"ppa": "false"
}
},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Set *default* container specific settings.json values on container create.
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python"
}
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "pip3 install --user -r requirements.txt",
// Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}

View File

@@ -102,6 +102,10 @@ CUSTOM_SEARCH_ENGINE_ID=your-custom-search-engine-id
# USE_MAC_OS_TTS - Use Mac OS TTS or not (Default: False) # USE_MAC_OS_TTS - Use Mac OS TTS or not (Default: False)
USE_MAC_OS_TTS=False USE_MAC_OS_TTS=False
### STREAMELEMENTS
# USE_BRIAN_TTS - Use Brian TTS or not (Default: False)
USE_BRIAN_TTS=False
### ELEVENLABS ### ELEVENLABS
# ELEVENLABS_API_KEY - Eleven Labs API key (Example: my-elevenlabs-api-key) # ELEVENLABS_API_KEY - Eleven Labs API key (Example: my-elevenlabs-api-key)
# ELEVENLABS_VOICE_1_ID - Eleven Labs voice 1 ID (Example: my-voice-id-1) # ELEVENLABS_VOICE_1_ID - Eleven Labs voice 1 ID (Example: my-voice-id-1)

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ last_run_ai_settings.yaml
auto-gpt.json auto-gpt.json
log.txt log.txt
log-ingestion.txt log-ingestion.txt
logs
# Coverage reports # Coverage reports
.coverage .coverage

View File

@@ -1,7 +1,23 @@
# Use an official Python base image from the Docker Hub
FROM python:3.11-slim FROM python:3.11-slim
ENV PIP_NO_CACHE_DIR=yes
WORKDIR /app # Set environment variables
COPY requirements.txt . ENV PIP_NO_CACHE_DIR=yes \
RUN pip install -r requirements.txt PYTHONUNBUFFERED=1 \
COPY scripts/ . PYTHONDONTWRITEBYTECODE=1
ENTRYPOINT ["python", "main.py"]
# Create a non-root user and set permissions
RUN useradd --create-home appuser
WORKDIR /home/appuser
RUN chown appuser:appuser /home/appuser
USER appuser
# Copy the requirements.txt file and install the requirements
COPY --chown=appuser:appuser requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# Copy the application files
COPY --chown=appuser:appuser scripts/ .
# Set the entrypoint
ENTRYPOINT ["python", "main.py"]

View File

@@ -66,13 +66,16 @@ Your support is greatly appreciated
## 📋 Requirements ## 📋 Requirements
- [Python 3.8 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows) - environments(just choose one)
- [vscode + devcontainer](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers): It has been configured in the .devcontainer folder and can be used directly
- [Python 3.8 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows)
- [OpenAI API key](https://platform.openai.com/account/api-keys) - [OpenAI API key](https://platform.openai.com/account/api-keys)
- [PINECONE API key](https://www.pinecone.io/)
Optional: Optional:
- [ElevenLabs Key](https://elevenlabs.io/) (If you want the AI to speak) - [PINECONE API key](https://www.pinecone.io/) (If you want Pinecone backed memory)
- ElevenLabs Key (If you want the AI to speak)
## 💾 Installation ## 💾 Installation
@@ -123,8 +126,8 @@ pip install -r requirements.txt
python scripts/main.py python scripts/main.py
``` ```
2. After each of AUTO-GPT's actions, type "NEXT COMMAND" to authorise them to continue. 2. After each of action, enter 'y' to authorise command, 'y -N' to run N continuous commands, 'n' to exit program, or enter additional feedback for the AI.
3. To exit the program, type "exit" and press Enter.
### Logs ### Logs
@@ -155,9 +158,10 @@ To use the `google_official_search` command, you need to set up your Google API
4. Go to the [APIs & Services Dashboard](https://console.cloud.google.com/apis/dashboard) and click "Enable APIs and Services". Search for "Custom Search API" and click on it, then click "Enable". 4. Go to the [APIs & Services Dashboard](https://console.cloud.google.com/apis/dashboard) and click "Enable APIs and Services". Search for "Custom Search API" and click on it, then click "Enable".
5. Go to the [Credentials](https://console.cloud.google.com/apis/credentials) page and click "Create Credentials". Choose "API Key". 5. Go to the [Credentials](https://console.cloud.google.com/apis/credentials) page and click "Create Credentials". Choose "API Key".
6. Copy the API key and set it as an environment variable named `GOOGLE_API_KEY` on your machine. See setting up environment variables below. 6. Copy the API key and set it as an environment variable named `GOOGLE_API_KEY` on your machine. See setting up environment variables below.
7. Go to the [Custom Search Engine](https://cse.google.com/cse/all) page and click "Add". 7. [Enable](https://console.developers.google.com/apis/api/customsearch.googleapis.com) the Custom Search API on your project. (Might need to wait few minutes to propagate)
8. Set up your search engine by following the prompts. You can choose to search the entire web or specific sites. 8. Go to the [Custom Search Engine](https://cse.google.com/cse/all) page and click "Add".
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. 9. Set up your search engine by following the prompts. You can choose to search the entire web or specific sites.
10. 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 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._ _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._
@@ -226,7 +230,10 @@ Pinecone enables the storage of vast amounts of vector-based memory, allowing fo
### Setting up environment variables ### Setting up environment variables
Simply set them in the `.env` file. In the `.env` file set:
- `PINECONE_API_KEY`
- `PINECONE_ENV` (something like: us-east4-gcp)
- `MEMORY_BACKEND=pinecone`
Alternatively, you can set them from the command line (advanced): Alternatively, you can set them from the command line (advanced):
@@ -235,7 +242,7 @@ For Windows Users:
``` ```
setx PINECONE_API_KEY "YOUR_PINECONE_API_KEY" setx PINECONE_API_KEY "YOUR_PINECONE_API_KEY"
setx PINECONE_ENV "Your pinecone region" # something like: us-east4-gcp setx PINECONE_ENV "Your pinecone region" # something like: us-east4-gcp
setx MEMORY_BACKEND "pinecone"
``` ```
For macOS and Linux users: For macOS and Linux users:
@@ -243,7 +250,7 @@ For macOS and Linux users:
``` ```
export PINECONE_API_KEY="YOUR_PINECONE_API_KEY" export PINECONE_API_KEY="YOUR_PINECONE_API_KEY"
export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp export PINECONE_ENV="Your pinecone region" # something like: us-east4-gcp
export MEMORY_BACKEND="pinecone"
``` ```
## Setting Your Cache Type ## Setting Your Cache Type
@@ -404,4 +411,4 @@ flake8 scripts/ tests/
# Or, if you want to run flake8 with the same configuration as the CI: # Or, if you want to run flake8 with the same configuration as the CI:
flake8 scripts/ tests/ --select E303,W293,W291,W292,E305,E231,E302 flake8 scripts/ tests/ --select E303,W293,W291,W292,E305,E231,E302
``` ```

View File

@@ -70,8 +70,8 @@ class AIConfig:
""" """
config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals} config = {"ai_name": self.ai_name, "ai_role": self.ai_role, "ai_goals": self.ai_goals}
with open(config_file, "w") as file: with open(config_file, "w", encoding='utf-8') as file:
yaml.dump(config, file) yaml.dump(config, file, allow_unicode=True)
def construct_full_prompt(self) -> str: def construct_full_prompt(self) -> str:
""" """

View File

@@ -13,7 +13,7 @@ def call_ai_function(function, args, description, model=None):
model = cfg.smart_llm_model model = cfg.smart_llm_model
# For each arg, if any are None, convert to "None": # For each arg, if any are None, convert to "None":
args = [str(arg) if arg is not None else "None" for arg in args] args = [str(arg) if arg is not None else "None" for arg in args]
# parse args to comma seperated string # parse args to comma separated string
args = ", ".join(args) args = ", ".join(args)
messages = [ messages = [
{ {

View File

@@ -62,6 +62,9 @@ class Config(metaclass=Singleton):
self.use_mac_os_tts = False self.use_mac_os_tts = False
self.use_mac_os_tts = os.getenv("USE_MAC_OS_TTS") self.use_mac_os_tts = os.getenv("USE_MAC_OS_TTS")
self.use_brian_tts = False
self.use_brian_tts = os.getenv("USE_BRIAN_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")

View File

@@ -24,7 +24,8 @@ For console handler: simulates typing
class Logger(metaclass=Singleton): class Logger(metaclass=Singleton):
def __init__(self): def __init__(self):
# create log directory if it doesn't exist # create log directory if it doesn't exist
log_dir = os.path.join('..', 'logs') this_files_dir_path = os.path.dirname(__file__)
log_dir = os.path.join(this_files_dir_path, '../logs')
if not os.path.exists(log_dir): if not os.path.exists(log_dir):
os.makedirs(log_dir) os.makedirs(log_dir)

View File

@@ -129,60 +129,6 @@ def print_assistant_thoughts(assistant_reply):
logger.error("Error: \n", call_stack) logger.error("Error: \n", call_stack)
def load_variables(config_file="config.yaml"):
"""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)
ai_name = config.get("ai_name")
ai_role = config.get("ai_role")
ai_goals = config.get("ai_goals")
except FileNotFoundError:
ai_name = ""
ai_role = ""
ai_goals = []
# Prompt the user for input if config file is missing or empty values
if not ai_name:
ai_name = utils.clean_input("Name your AI: ")
if ai_name == "":
ai_name = "Entrepreneur-GPT"
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."
if not ai_goals:
print("Enter up to 5 goals for your AI: ")
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 = utils.clean_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)
prompt = get_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."""
# 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"
full_prompt += f"\n\n{prompt}"
return full_prompt
def construct_prompt(): def construct_prompt():
"""Construct the prompt for the AI to respond to""" """Construct the prompt for the AI to respond to"""
config = AIConfig.load() config = AIConfig.load()
@@ -318,10 +264,6 @@ def parse_arguments():
logger.typewriter_log("GPT4 Only Mode: ", Fore.GREEN, "ENABLED") logger.typewriter_log("GPT4 Only Mode: ", Fore.GREEN, "ENABLED")
cfg.set_fast_llm_model(cfg.smart_llm_model) cfg.set_fast_llm_model(cfg.smart_llm_model)
if args.debug:
logger.typewriter_log("Debug Mode: ", Fore.GREEN, "ENABLED")
cfg.set_debug_mode(True)
if args.memory_type: if args.memory_type:
supported_memory = get_supported_memory_backends() supported_memory = get_supported_memory_backends()
chosen = args.memory_type chosen = args.memory_type
@@ -351,110 +293,148 @@ def main():
# this is particularly important for indexing and referencing pinecone memory # this is particularly important for indexing and referencing pinecone memory
memory = get_memory(cfg, init=True) memory = get_memory(cfg, init=True)
print('Using memory of type: ' + memory.__class__.__name__) print('Using memory of type: ' + memory.__class__.__name__)
# Interaction Loop agent = Agent(
loop_count = 0 ai_name=ai_name,
while True: memory=memory,
# Discontinue if continuous limit is reached full_message_history=full_message_history,
loop_count += 1 next_action_count=next_action_count,
if cfg.continuous_mode and cfg.continuous_limit > 0 and loop_count > cfg.continuous_limit: prompt=prompt,
logger.typewriter_log("Continuous Limit Reached: ", Fore.YELLOW, f"{cfg.continuous_limit}") user_input=user_input
break )
agent.start_interaction_loop()
# Send message to AI, get response
with Spinner("Thinking... "):
assistant_reply = chat.chat_with_ai(
prompt,
user_input,
full_message_history,
memory,
cfg.fast_token_limit) # TODO: This hardcodes the model to use GPT3.5. Make this an argument
# Print Assistant thoughts class Agent:
print_assistant_thoughts(assistant_reply) """Agent class for interacting with Auto-GPT.
# Get command name and arguments Attributes:
try: ai_name: The name of the agent.
command_name, arguments = cmd.get_command( memory: The memory object to use.
attempt_to_fix_json_by_finding_outermost_brackets(assistant_reply)) full_message_history: The full message history.
if cfg.speak_mode: next_action_count: The number of actions to execute.
speak.say_text(f"I want to execute {command_name}") prompt: The prompt to use.
except Exception as e: user_input: The user input.
logger.error("Error: \n", str(e))
if not cfg.continuous_mode and next_action_count == 0: """
### GET USER AUTHORIZATION TO EXECUTE COMMAND ### def __init__(self,
# Get key press: Prompt the user to press enter to continue or escape ai_name,
# to exit memory,
user_input = "" full_message_history,
logger.typewriter_log( next_action_count,
"NEXT ACTION: ", prompt,
Fore.CYAN, user_input):
f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}") self.ai_name = ai_name
print( self.memory = memory
f"Enter 'y' to authorise command, 'y -N' to run N continuous commands, 'n' to exit program, or enter feedback for {ai_name}...", self.full_message_history = full_message_history
flush=True) self.next_action_count = next_action_count
while True: self.prompt = prompt
console_input = utils.clean_input(Fore.MAGENTA + "Input:" + Style.RESET_ALL) self.user_input = user_input
if console_input.lower().rstrip() == "y":
user_input = "GENERATE NEXT COMMAND JSON"
break
elif console_input.lower().startswith("y -"):
try:
next_action_count = abs(int(console_input.split(" ")[1]))
user_input = "GENERATE NEXT COMMAND JSON"
except ValueError:
print("Invalid input format. Please enter 'y -n' where n is the number of continuous tasks.")
continue
break
elif console_input.lower() == "n":
user_input = "EXIT"
break
else:
user_input = console_input
command_name = "human_feedback"
break
if user_input == "GENERATE NEXT COMMAND JSON": def start_interaction_loop(self):
logger.typewriter_log( # Interaction Loop
"-=-=-=-=-=-=-= COMMAND AUTHORISED BY USER -=-=-=-=-=-=-=", loop_count = 0
Fore.MAGENTA, while True:
"") # Discontinue if continuous limit is reached
elif user_input == "EXIT": loop_count += 1
print("Exiting...", flush=True) if cfg.continuous_mode and cfg.continuous_limit > 0 and loop_count > cfg.continuous_limit:
logger.typewriter_log("Continuous Limit Reached: ", Fore.YELLOW, f"{cfg.continuous_limit}")
break break
else:
# Print command
logger.typewriter_log(
"NEXT ACTION: ",
Fore.CYAN,
f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}")
# Execute command # Send message to AI, get response
if command_name is not None and command_name.lower().startswith("error"): with Spinner("Thinking... "):
result = f"Command {command_name} threw the following error: " + arguments assistant_reply = chat.chat_with_ai(
elif command_name == "human_feedback": self.prompt,
result = f"Human feedback: {user_input}" self.user_input,
else: self.full_message_history,
result = f"Command {command_name} returned: {cmd.execute_command(command_name, arguments)}" self.memory,
if next_action_count > 0: cfg.fast_token_limit) # TODO: This hardcodes the model to use GPT3.5. Make this an argument
next_action_count -= 1
memory_to_add = f"Assistant Reply: {assistant_reply} " \ # Print Assistant thoughts
f"\nResult: {result} " \ print_assistant_thoughts(assistant_reply)
f"\nHuman Feedback: {user_input} "
memory.add(memory_to_add) # Get command name and arguments
try:
command_name, arguments = cmd.get_command(
attempt_to_fix_json_by_finding_outermost_brackets(assistant_reply))
if cfg.speak_mode:
speak.say_text(f"I want to execute {command_name}")
except Exception as e:
logger.error("Error: \n", str(e))
# Check if there's a result from the command append it to the message if not cfg.continuous_mode and self.next_action_count == 0:
# history ### GET USER AUTHORIZATION TO EXECUTE COMMAND ###
if result is not None: # Get key press: Prompt the user to press enter to continue or escape
full_message_history.append(chat.create_chat_message("system", result)) # to exit
logger.typewriter_log("SYSTEM: ", Fore.YELLOW, result) self.user_input = ""
else: logger.typewriter_log(
full_message_history.append( "NEXT ACTION: ",
chat.create_chat_message( Fore.CYAN,
"system", "Unable to execute command")) f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}")
logger.typewriter_log("SYSTEM: ", Fore.YELLOW, "Unable to execute command") print(
f"Enter 'y' to authorise command, 'y -N' to run N continuous commands, 'n' to exit program, or enter feedback for {self.ai_name}...",
flush=True)
while True:
console_input = utils.clean_input(Fore.MAGENTA + "Input:" + Style.RESET_ALL)
if console_input.lower().rstrip() == "y":
self.user_input = "GENERATE NEXT COMMAND JSON"
break
elif console_input.lower().startswith("y -"):
try:
self.next_action_count = abs(int(console_input.split(" ")[1]))
self.user_input = "GENERATE NEXT COMMAND JSON"
except ValueError:
print("Invalid input format. Please enter 'y -n' where n is the number of continuous tasks.")
continue
break
elif console_input.lower() == "n":
self.user_input = "EXIT"
break
else:
self.user_input = console_input
command_name = "human_feedback"
break
if self.user_input == "GENERATE NEXT COMMAND JSON":
logger.typewriter_log(
"-=-=-=-=-=-=-= COMMAND AUTHORISED BY USER -=-=-=-=-=-=-=",
Fore.MAGENTA,
"")
elif self.user_input == "EXIT":
print("Exiting...", flush=True)
break
else:
# Print command
logger.typewriter_log(
"NEXT ACTION: ",
Fore.CYAN,
f"COMMAND = {Fore.CYAN}{command_name}{Style.RESET_ALL} ARGUMENTS = {Fore.CYAN}{arguments}{Style.RESET_ALL}")
# Execute command
if command_name is not None and command_name.lower().startswith("error"):
result = f"Command {command_name} threw the following error: " + arguments
elif command_name == "human_feedback":
result = f"Human feedback: {self.user_input}"
else:
result = f"Command {command_name} returned: {cmd.execute_command(command_name, arguments)}"
if self.next_action_count > 0:
self.next_action_count -= 1
memory_to_add = f"Assistant Reply: {assistant_reply} " \
f"\nResult: {result} " \
f"\nHuman Feedback: {self.user_input} "
self.memory.add(memory_to_add)
# Check if there's a result from the command append it to the message
# history
if result is not None:
self.full_message_history.append(chat.create_chat_message("system", result))
logger.typewriter_log("SYSTEM: ", Fore.YELLOW, result)
else:
self.full_message_history.append(
chat.create_chat_message(
"system", "Unable to execute command"))
logger.typewriter_log("SYSTEM: ", Fore.YELLOW, "Unable to execute command")
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -53,6 +53,24 @@ def eleven_labs_speech(text, voice_index=0):
return False return False
def brian_speech(text):
"""Speak text using Brian with the streamelements API"""
tts_url = f"https://api.streamelements.com/kappa/v2/speech?voice=Brian&text={text}"
response = requests.get(tts_url)
if response.status_code == 200:
with mutex_lock:
with open("speech.mp3", "wb") as f:
f.write(response.content)
playsound("speech.mp3")
os.remove("speech.mp3")
return True
else:
print("Request failed with status code:", response.status_code)
print("Response content:", response.content)
return False
def gtts_speech(text): def gtts_speech(text):
tts = gtts.gTTS(text) tts = gtts.gTTS(text)
with mutex_lock: with mutex_lock:
@@ -76,7 +94,11 @@ def say_text(text, voice_index=0):
def speak(): def speak():
if not cfg.elevenlabs_api_key: if not cfg.elevenlabs_api_key:
if cfg.use_mac_os_tts == 'True': if cfg.use_mac_os_tts == 'True':
macos_tts_speech(text, voice_index) macos_tts_speech(text)
elif cfg.use_brian_tts == 'True':
success = brian_speech(text)
if not success:
gtts_speech(text)
else: else:
gtts_speech(text) gtts_speech(text)
else: else:

View File

@@ -17,10 +17,10 @@ class Spinner:
def spin(self): def spin(self):
"""Spin the spinner""" """Spin the spinner"""
while self.running: while self.running:
sys.stdout.write(next(self.spinner) + " " + self.message + "\r") sys.stdout.write(f"{next(self.spinner)} {self.message}\r")
sys.stdout.flush() sys.stdout.flush()
time.sleep(self.delay) time.sleep(self.delay)
sys.stdout.write('\r' + ' ' * (len(self.message) + 2) + '\r') sys.stdout.write(f"\r{' ' * (len(self.message) + 2)}\r")
def __enter__(self): def __enter__(self):
"""Start the spinner""" """Start the spinner"""
@@ -32,5 +32,5 @@ class Spinner:
"""Stop the spinner""" """Stop the spinner"""
self.running = False self.running = False
self.spinner_thread.join() self.spinner_thread.join()
sys.stdout.write('\r' + ' ' * (len(self.message) + 2) + '\r') sys.stdout.write(f"\r{' ' * (len(self.message) + 2)}\r")
sys.stdout.flush() sys.stdout.flush()