Add text input prompt to TUI - alternative to voice commands
This commit is contained in:
parent
4f662ee81b
commit
530944f166
140
tui_app.py
140
tui_app.py
@ -1,10 +1,14 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Text User Interface (TUI) for Voice Assistant - Simple Version
|
Text User Interface (TUI) for Voice Assistant with Text Input
|
||||||
AI Now Inc - Del Mar Demo Unit
|
AI Now Inc - Del Mar Demo Unit
|
||||||
Laboratory Assistant: Claw 🏭
|
Laboratory Assistant: Claw 🏭
|
||||||
|
|
||||||
This version uses the existing working components from main.py
|
Features:
|
||||||
|
- Voice activation via "Hey Osiris"
|
||||||
|
- Text input alternative (type commands)
|
||||||
|
- Rich terminal formatting
|
||||||
|
- Conversation history
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -13,6 +17,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -21,6 +26,7 @@ try:
|
|||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
from rich import box
|
from rich import box
|
||||||
|
from rich.prompt import Prompt
|
||||||
RICH_AVAILABLE = True
|
RICH_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
RICH_AVAILABLE = False
|
RICH_AVAILABLE = False
|
||||||
@ -29,6 +35,7 @@ except ImportError:
|
|||||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'rich'])
|
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'rich'])
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
|
from rich.prompt import Prompt
|
||||||
from rich import box
|
from rich import box
|
||||||
RICH_AVAILABLE = True
|
RICH_AVAILABLE = True
|
||||||
print("Rich installed!")
|
print("Rich installed!")
|
||||||
@ -46,7 +53,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class TUIAssistant:
|
class TUIAssistant:
|
||||||
"""Simple TUI-based voice assistant."""
|
"""TUI-based voice assistant with text input support."""
|
||||||
|
|
||||||
def __init__(self, config_path: str = "config.json"):
|
def __init__(self, config_path: str = "config.json"):
|
||||||
self.config_path = Path(config_path)
|
self.config_path = Path(config_path)
|
||||||
@ -62,8 +69,9 @@ class TUIAssistant:
|
|||||||
|
|
||||||
# State
|
# State
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
|
self.voice_mode = False # True when voice activation is active
|
||||||
self.conversation_history = []
|
self.conversation_history = []
|
||||||
self.max_history = 10
|
self.max_history = 20
|
||||||
|
|
||||||
# Rich console
|
# Rich console
|
||||||
self.console = Console()
|
self.console = Console()
|
||||||
@ -94,19 +102,85 @@ class TUIAssistant:
|
|||||||
def _on_hotword(self):
|
def _on_hotword(self):
|
||||||
"""Callback when hotword is detected."""
|
"""Callback when hotword is detected."""
|
||||||
self.console.print("\n[bold green]✨ Hotword detected! ✨[/bold green]")
|
self.console.print("\n[bold green]✨ Hotword detected! ✨[/bold green]")
|
||||||
|
self.voice_mode = True
|
||||||
self.tts.speak("Yes?", "en")
|
self.tts.speak("Yes?", "en")
|
||||||
|
|
||||||
def _add_to_history(self, role: str, text: str):
|
def _add_to_history(self, role: str, text: str, method: str = "text"):
|
||||||
"""Add message to conversation history."""
|
"""Add message to conversation history."""
|
||||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||||
self.conversation_history.append({
|
self.conversation_history.append({
|
||||||
'timestamp': timestamp,
|
'timestamp': timestamp,
|
||||||
'role': role,
|
'role': role,
|
||||||
'text': text
|
'text': text,
|
||||||
|
'method': method # 'text', 'voice', or 'assistant'
|
||||||
})
|
})
|
||||||
if len(self.conversation_history) > self.max_history:
|
if len(self.conversation_history) > self.max_history:
|
||||||
self.conversation_history.pop(0)
|
self.conversation_history.pop(0)
|
||||||
|
|
||||||
|
def _display_history(self):
|
||||||
|
"""Display recent conversation history."""
|
||||||
|
if len(self.conversation_history) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.console.print("\n[bold]📜 Recent Conversation:[/bold]")
|
||||||
|
for msg in self.conversation_history[-10:]: # Show last 10 messages
|
||||||
|
if msg['role'] == 'user':
|
||||||
|
icon = "👤" if msg['method'] == 'text' else "🎤"
|
||||||
|
color = "green" if msg['method'] == 'text' else "yellow"
|
||||||
|
self.console.print(f"[{color}]{icon} [{msg['timestamp']}]: {msg['text']}")
|
||||||
|
else:
|
||||||
|
self.console.print(f"[cyan]🤖 [{msg['timestamp']}]: {msg['text']}")
|
||||||
|
self.console.print()
|
||||||
|
|
||||||
|
def _process_command(self, command: str, method: str = "text"):
|
||||||
|
"""Process a command from user (text or voice)."""
|
||||||
|
if not command or command.strip() == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
self._add_to_history('user', command, method)
|
||||||
|
|
||||||
|
# Check for special commands
|
||||||
|
cmd_lower = command.lower().strip()
|
||||||
|
if cmd_lower in ['quit', 'exit', 'bye']:
|
||||||
|
self.console.print("\n[bold]Goodbye! 👋[/bold]")
|
||||||
|
self.tts.speak("Goodbye!", "en")
|
||||||
|
self.is_running = False
|
||||||
|
return
|
||||||
|
elif cmd_lower == 'history':
|
||||||
|
self._display_history()
|
||||||
|
return
|
||||||
|
elif cmd_lower == 'help':
|
||||||
|
self._show_help()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Process with assistant
|
||||||
|
self.console.print("\n[bold blue]🤔 Processing...[/bold blue]")
|
||||||
|
response = self.assistant.process(command)
|
||||||
|
|
||||||
|
self._add_to_history('assistant', response, 'assistant')
|
||||||
|
self.console.print(f"[bold cyan]🤖:[/bold cyan] {response}")
|
||||||
|
|
||||||
|
# Speak response
|
||||||
|
self.console.print("[bold blue]🔊 Speaking...[/bold blue]")
|
||||||
|
self.tts.speak(response, "en")
|
||||||
|
|
||||||
|
def _show_help(self):
|
||||||
|
"""Show help message."""
|
||||||
|
help_text = """
|
||||||
|
[bold]Voice Assistant Commands:[/bold]
|
||||||
|
• Type your command or question
|
||||||
|
• Say "Hey Osiris" for voice activation
|
||||||
|
• Type 'history' to see recent conversation
|
||||||
|
• Type 'quit' or 'exit' to stop
|
||||||
|
|
||||||
|
[bold]Examples:[/bold]
|
||||||
|
• "What time is it?"
|
||||||
|
• "Play some music"
|
||||||
|
• "Tell me a joke"
|
||||||
|
• "What's the weather?"
|
||||||
|
"""
|
||||||
|
self.console.print(Panel(help_text, box=box.ROUNDED, border_style="green"))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run the TUI voice assistant."""
|
"""Run the TUI voice assistant."""
|
||||||
logger.info("Starting TUI voice assistant...")
|
logger.info("Starting TUI voice assistant...")
|
||||||
@ -115,32 +189,30 @@ class TUIAssistant:
|
|||||||
self.console.print(Panel.fit(
|
self.console.print(Panel.fit(
|
||||||
"[bold]🎤 Voice Assistant - AI Now Inc[/bold]\n"
|
"[bold]🎤 Voice Assistant - AI Now Inc[/bold]\n"
|
||||||
"[cyan]Laboratory Assistant: Claw 🏭[/cyan]\n\n"
|
"[cyan]Laboratory Assistant: Claw 🏭[/cyan]\n\n"
|
||||||
"Say '[bold]Hey Osiris[/bold]' or '[bold]你好 Osiris[/bold]' to activate\n"
|
"Modes:\n"
|
||||||
"Press [bold]Ctrl+C[/bold] to stop",
|
" • [bold]Type commands[/bold] in the prompt\n"
|
||||||
|
" • Say '[bold]Hey Osiris[/bold]' for voice activation\n\n"
|
||||||
|
"Commands: [bold]help[/bold] | [bold]history[/bold] | [bold]quit[/bold]",
|
||||||
box=box.ROUNDED,
|
box=box.ROUNDED,
|
||||||
border_style="blue"
|
border_style="blue"
|
||||||
))
|
))
|
||||||
|
|
||||||
|
self._show_help()
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
self.console.print("\n[bold blue]→[/bold blue] Listening for hotword...")
|
|
||||||
|
|
||||||
# Main loop - use hotword detector's blocking detect
|
# Start voice listener in background
|
||||||
|
voice_thread = threading.Thread(target=self._voice_listener, daemon=True)
|
||||||
|
voice_thread.start()
|
||||||
|
|
||||||
|
# Main loop - text input
|
||||||
while self.is_running:
|
while self.is_running:
|
||||||
try:
|
try:
|
||||||
# Wait for hotword (this blocks until detected or timeout)
|
# Get text input from user
|
||||||
detected_keyword = self.hotword_detector.detect(timeout=30)
|
prompt_text = "\n[bold green]👤 You:[/bold green] " if not self.voice_mode else "\n[bold yellow]🎤 Voice Mode:[/bold yellow] "
|
||||||
|
user_input = Prompt.ask(prompt_text)
|
||||||
|
|
||||||
if detected_keyword:
|
if user_input:
|
||||||
self.console.print(f"\n[bold green]✓[/bold green] Heard: '{detected_keyword}'")
|
self._process_command(user_input, method='text')
|
||||||
|
|
||||||
# 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:
|
except KeyboardInterrupt:
|
||||||
logger.info("Interrupted by user")
|
logger.info("Interrupted by user")
|
||||||
@ -151,6 +223,28 @@ class TUIAssistant:
|
|||||||
|
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
|
|
||||||
|
def _voice_listener(self):
|
||||||
|
"""Background thread to listen for voice hotword."""
|
||||||
|
logger.info("Voice listener started")
|
||||||
|
|
||||||
|
while self.is_running:
|
||||||
|
try:
|
||||||
|
if self.voice_mode:
|
||||||
|
# Voice mode active - wait for speech
|
||||||
|
# In a full implementation, this would use speech recognition
|
||||||
|
time.sleep(0.5)
|
||||||
|
self.voice_mode = False # Reset after use
|
||||||
|
else:
|
||||||
|
# Wait for hotword
|
||||||
|
detected = self.hotword_detector.detect(timeout=5)
|
||||||
|
if detected:
|
||||||
|
self._on_hotword()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Voice listener error: {e}")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
logger.info("Voice listener stopped")
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
"""Clean shutdown."""
|
"""Clean shutdown."""
|
||||||
logger.info("Shutting down...")
|
logger.info("Shutting down...")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user