Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GROQ_API_KEY=your_groq_api_key_here
LLM_PROVIDER=groq
23 changes: 18 additions & 5 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@ BonziAssist revives the classic BonziBuddy assistant in a modern, voice-activate

### Installation
1. Clone this repository.
2. Ensure you have [Python](https://www.python.org/downloads/) and [Visual CPP Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/). installed, and then run `pip install -r requirements.txt` to install the necessary packages.
4. Set up your environment variables. Edit the `.env.example` file in `helpers\` and follow the instructions there. `[INSTRUCTIONS='Remove this line of instructions. Fill out the info below. Rename this file, and remove the .example. It should just be ".env" now.']`
2. Ensure you have [Python](https://www.python.org/downloads/) and [Visual CPP Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) installed.
3. Install the required packages:
```bash
python -m pip install -r requirements.txt
```
4. Set up your environment variables:
```bash
copy .env.example .env
```
Then open `.env` and set `GROQ_API_KEY` and `LLM_PROVIDER`.

### Usage
To start BonziAssist:
Before starting BonziAssist, run the setup check:
```bash
python start.py --check
```

If the check passes, start BonziAssist:
```bash
python main.py
python start.py
```

You will be prompted to configure your microphone settings upon the first run or if you choose to be prompted every time by the configuration settings.
Expand Down Expand Up @@ -60,4 +73,4 @@ First of all, I appreciate anyone being excited about this project. That being s
- **SimpleAudio**: Used for playing back audio responses generated by the system.
- **PyAudio**: Handles microphone input and audio stream management.
- **Python-dotenv**: For managing environment variables in a .env file, which helps secure API keys and other sensitive settings.
- **GitHub Community**: For all the open-source contributors who have developed libraries and tools that made this project feasible.
- **GitHub Community**: For all the open-source contributors who have developed libraries and tools that made this project feasible.
11 changes: 7 additions & 4 deletions helpers/llm.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import os
from pathlib import Path

from dotenv import load_dotenv
import litellm

load_dotenv()
PROJECT_ROOT = Path(__file__).resolve().parents[1]
load_dotenv(PROJECT_ROOT / ".env")

groq_api_key = os.getenv("GROQ_API_KEY")
llm_provider = os.getenv("LLM_PROVIDER")

if groq_api_key is None:
raise ValueError("GROQ_API_KEY environment variable not found. Please set it in your .env file.")
raise ValueError("GROQ_API_KEY environment variable not found. Copy .env.example to .env and set your key.")

if llm_provider is None:
raise ValueError("LLM_PROVIDER environment variable not found. Please set it in your .env file.")
raise ValueError("LLM_PROVIDER environment variable not found. Copy .env.example to .env and set your provider.")

os.environ["GROQ_API_KEY"] = groq_api_key

Expand All @@ -34,4 +37,4 @@ def request(user_query, model="llama3-8b-8192", temperature=0.7, max_tokens=150)
max_tokens=max_tokens
)

return response.choices[0].message.content
return response.choices[0].message.content
21 changes: 14 additions & 7 deletions helpers/mic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import pyaudio
import os
import json
from pathlib import Path

import pyaudio

CONFIG_FILE = "config\mic_config.json"
PROJECT_ROOT = Path(__file__).resolve().parents[1]
CONFIG_FILE = PROJECT_ROOT / "config" / "mic_config.json"
LEGACY_CONFIG_FILE = PROJECT_ROOT / "mic_config.json"

def list_microphones():
p = pyaudio.PyAudio()
Expand Down Expand Up @@ -34,13 +37,17 @@ def get_device_index(num_devices, default_device_index, default_device_name):
print("Invalid input. Please enter a valid number.")

def load_config():
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "r") as f:
if CONFIG_FILE.exists():
with CONFIG_FILE.open("r") as f:
return json.load(f)
if LEGACY_CONFIG_FILE.exists():
with LEGACY_CONFIG_FILE.open("r") as f:
return json.load(f)
return None

def save_config(config):
with open(CONFIG_FILE, "w") as f:
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
with CONFIG_FILE.open("w") as f:
json.dump(config, f, indent=4)

def configure_microphone():
Expand All @@ -57,4 +64,4 @@ def configure_microphone():
"prompt_every_time": prompt_every_time == 'y'
}
save_config(config)
return config
return config
5 changes: 4 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ def listen_for_bonzi(device_index=None):
time.sleep(.45)
command_active = True # Enable command capture

if __name__ == "__main__":
def main():
config = mic.load_config()
if config is None or config.get("prompt_every_time", False):
config = mic.configure_microphone()
device_index = config['device_index']
listen_for_bonzi(device_index)

if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pyaudio
python-dotenv
litellm
requests
simpleaudio
vosk
103 changes: 103 additions & 0 deletions start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import argparse
import importlib.util
import os
import sys
from pathlib import Path


PROJECT_ROOT = Path(__file__).resolve().parent
REQUIRED_PACKAGES = {
"dotenv": "python-dotenv",
"litellm": "litellm",
"pyaudio": "pyaudio",
"requests": "requests",
"simpleaudio": "simpleaudio",
"vosk": "vosk",
}


def missing_packages():
missing = []
for import_name, package_name in REQUIRED_PACKAGES.items():
if importlib.util.find_spec(import_name) is None:
missing.append(package_name)
return missing


def load_env_file():
env_path = PROJECT_ROOT / ".env"
values = {}
if not env_path.exists():
return values

for line in env_path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
values[key.strip()] = value.strip()
return values


def check_setup():
errors = []
warnings = []

missing = missing_packages()
if missing:
errors.append(
"Install missing packages with: "
+ f"{sys.executable} -m pip install -r requirements.txt"
+ f" (missing: {', '.join(missing)})"
)

env_path = PROJECT_ROOT / ".env"
env_values = load_env_file()
if not env_path.exists():
errors.append("Create .env by copying .env.example, then set GROQ_API_KEY and LLM_PROVIDER.")
else:
for key in ("GROQ_API_KEY", "LLM_PROVIDER"):
if not env_values.get(key) or env_values[key].startswith("your_"):
errors.append(f"Set {key} in .env.")

model_path = PROJECT_ROOT / "vosk" / "vosk-model-small-en-us-0.15"
if not model_path.exists():
errors.append("The Vosk model is missing. Expected: vosk/vosk-model-small-en-us-0.15")

if not (PROJECT_ROOT / "canned_responses").exists():
errors.append("The canned_responses folder is missing.")

if not (PROJECT_ROOT / "config").exists():
warnings.append("The config folder will be created on first microphone setup.")

return errors, warnings


def main():
parser = argparse.ArgumentParser(description="Check and start BonziAssist.")
parser.add_argument("--check", action="store_true", help="Only check setup and exit.")
args = parser.parse_args()

os.chdir(PROJECT_ROOT)
errors, warnings = check_setup()

for warning in warnings:
print(f"Warning: {warning}")

if errors:
print("BonziAssist is not ready to start:")
for error in errors:
print(f"- {error}")
raise SystemExit(1)

print("BonziAssist setup looks ready.")
if args.check:
return

from main import main as run_bonzi

run_bonzi()


if __name__ == "__main__":
main()