180 lines
5.8 KiB
Python
180 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Text User Interface (TUI) for Voice Assistant - Simple Version
|
|
AI Now Inc - Del Mar Demo Unit
|
|
Laboratory Assistant: Claw 🏭
|
|
|
|
This version uses the existing working components from main.py
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import logging
|
|
import signal
|
|
import time
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
# Try to import rich for beautiful TUI
|
|
try:
|
|
from rich.console import Console
|
|
from rich.panel import Panel
|
|
from rich import box
|
|
RICH_AVAILABLE = True
|
|
except ImportError:
|
|
RICH_AVAILABLE = False
|
|
print("Installing rich for TUI...")
|
|
import subprocess
|
|
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'rich'])
|
|
from rich.console import Console
|
|
from rich.panel import Panel
|
|
from rich import box
|
|
RICH_AVAILABLE = True
|
|
print("Rich installed!")
|
|
|
|
from assistant import VoiceAssistant
|
|
from tts_engine import TTSEngine
|
|
from hotword_detector import HotwordDetector
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TUIAssistant:
|
|
"""Simple TUI-based voice assistant."""
|
|
|
|
def __init__(self, config_path: str = "config.json"):
|
|
self.config_path = Path(config_path)
|
|
self.config = self._load_config()
|
|
|
|
# Initialize components
|
|
logger.info("Initializing TUI voice assistant...")
|
|
self.assistant = VoiceAssistant(str(self.config_path))
|
|
self.tts = TTSEngine(str(self.config_path))
|
|
self.hotword_detector = HotwordDetector(
|
|
str(self.config_path).replace("config.json", "hotword_config.json")
|
|
)
|
|
|
|
# State
|
|
self.is_running = False
|
|
self.conversation_history = []
|
|
self.max_history = 10
|
|
|
|
# Rich console
|
|
self.console = Console()
|
|
|
|
# Setup signal handlers
|
|
signal.signal(signal.SIGINT, self._signal_handler)
|
|
signal.signal(signal.SIGTERM, self._signal_handler)
|
|
|
|
# Set callback for hotword detection
|
|
self.hotword_detector.set_callback(self._on_hotword)
|
|
|
|
logger.info("TUI Voice assistant initialized")
|
|
|
|
def _load_config(self) -> dict:
|
|
"""Load configuration."""
|
|
try:
|
|
with open(self.config_path, 'r') as f:
|
|
return json.load(f)
|
|
except FileNotFoundError:
|
|
logger.warning("Config not found, using defaults")
|
|
return {}
|
|
|
|
def _signal_handler(self, sig, frame):
|
|
"""Handle shutdown signals."""
|
|
logger.info("Shutdown signal received")
|
|
self.is_running = False
|
|
|
|
def _on_hotword(self):
|
|
"""Callback when hotword is detected."""
|
|
self.console.print("\n[bold green]✨ Hotword detected! ✨[/bold green]")
|
|
self.tts.speak("Yes?", "en")
|
|
|
|
def _add_to_history(self, role: str, text: str):
|
|
"""Add message to conversation history."""
|
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
self.conversation_history.append({
|
|
'timestamp': timestamp,
|
|
'role': role,
|
|
'text': text
|
|
})
|
|
if len(self.conversation_history) > self.max_history:
|
|
self.conversation_history.pop(0)
|
|
|
|
def run(self):
|
|
"""Run the TUI voice assistant."""
|
|
logger.info("Starting TUI voice assistant...")
|
|
|
|
# Display welcome screen
|
|
self.console.print(Panel.fit(
|
|
"[bold]🎤 Voice Assistant - AI Now Inc[/bold]\n"
|
|
"[cyan]Laboratory Assistant: Claw 🏭[/cyan]\n\n"
|
|
"Say '[bold]Hey Osiris[/bold]' or '[bold]你好 Osiris[/bold]' to activate\n"
|
|
"Press [bold]Ctrl+C[/bold] to stop",
|
|
box=box.ROUNDED,
|
|
border_style="blue"
|
|
))
|
|
|
|
self.is_running = True
|
|
self.console.print("\n[bold blue]→[/bold blue] Listening for hotword...")
|
|
|
|
# Main loop - use hotword detector's blocking detect
|
|
while self.is_running:
|
|
try:
|
|
# Wait for hotword (this blocks until detected or timeout)
|
|
detected_keyword = self.hotword_detector.detect(timeout=30)
|
|
|
|
if detected_keyword:
|
|
self.console.print(f"\n[bold green]✓[/bold green] Heard: '{detected_keyword}'")
|
|
|
|
# For now, just respond with a simple message
|
|
# In a full implementation, you'd use speech recognition here
|
|
response = "I heard you! What would you like me to do?"
|
|
self._add_to_history('assistant', response)
|
|
self.console.print(f"[bold cyan]🤖:[/bold cyan] {response}")
|
|
self.tts.speak(response, "en")
|
|
else:
|
|
self.console.print("[yellow]⏰ Timeout - trying again...[/yellow]")
|
|
|
|
except KeyboardInterrupt:
|
|
logger.info("Interrupted by user")
|
|
break
|
|
except Exception as e:
|
|
logger.error(f"Error: {e}")
|
|
time.sleep(1)
|
|
|
|
self.shutdown()
|
|
|
|
def shutdown(self):
|
|
"""Clean shutdown."""
|
|
logger.info("Shutting down...")
|
|
self.is_running = False
|
|
|
|
if hasattr(self, 'hotword_detector'):
|
|
self.hotword_detector.stop()
|
|
|
|
self.console.print("\n[bold red]Voice Assistant stopped.[/bold red]")
|
|
logger.info("Shutdown complete")
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='TUI Voice Assistant')
|
|
parser.add_argument('--config', default='config.json', help='Config file path')
|
|
args = parser.parse_args()
|
|
|
|
app = TUIAssistant(config_path=args.config)
|
|
app.run()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|