-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
233 lines (181 loc) Β· 7.17 KB
/
main.py
File metadata and controls
233 lines (181 loc) Β· 7.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
"""Beacon - Voice-First Browser Controller for Blind Users.
Refactored for simplicity and easy control flow management.
"""
import asyncio
import os
from dotenv import load_dotenv
from audio_tools import AudioHandler
from beacon_controller import BeaconController, BeaconState, AgentTask
from keyboard_handler import KeyboardHandler, InputEvent
from voice_handler import VoiceHandler
from config import (
DEFAULT_TTS_VOICE,
SHORTCUTS,
BANNER_WIDTH,
)
load_dotenv()
# Help text for users
HELP_TEXT = f"""
Beacon Help Guide.
Keyboard Shortcuts:
Press {SHORTCUTS['voice']} to start voice recording.
Press {SHORTCUTS['stop_audio']} to stop current audio playback.
Press {SHORTCUTS['help']} to hear this help message.
Press {SHORTCUTS['quit']} to quit Beacon.
Voice Commands:
You can ask Beacon to:
- Navigate to websites: "Go to amazon.com" or "Open YouTube"
- Summarize the page: "What is this page?" or "Summarize"
- Perform actions: "Click add to cart" or "Do number 2"
- Read content: "Read the main article" or "Read reviews"
- Fill forms: "Type John Doe in the name field"
- Search: "Search for wireless headphones"
- Go back: "Go back" or "Previous page"
Safety Features:
Beacon will always ask for confirmation before:
- Submitting payment information
- Posting or publishing content
- Deleting anything
- Making purchases
Examples:
"Open amazon.com" - Opens Amazon
"What can I do here?" - Summarizes available actions
"Do 1" - Performs the first action from the menu
"Read more details" - Reads additional content
"Help" - Plays this help message
Remember: Beacon is here to help you browse the web efficiently.
Just speak naturally and Beacon will understand your intent.
"""
class BeaconApp:
"""Main Beacon application that orchestrates all components.
This class provides a clean, easy-to-understand control flow.
"""
def __init__(self):
"""Initialize the Beacon application."""
# Initialize components
self.audio_handler = AudioHandler(
openai_api_key=os.getenv("OPENAI_API_KEY"),
voice=DEFAULT_TTS_VOICE
)
self.controller = BeaconController(
audio_handler=self.audio_handler,
gemini_api_key=os.getenv("GEMINI_API_KEY"),
verbose=True
)
self.keyboard = KeyboardHandler()
self.voice = VoiceHandler(self.audio_handler)
# Application state
self.running = False
# Setup callbacks for better control
self._setup_callbacks()
def _setup_callbacks(self):
"""Setup callbacks to monitor and control agent flow."""
async def on_state_change(old_state: BeaconState, new_state: BeaconState):
"""Called when controller state changes."""
print(f"π State: {old_state.value} β {new_state.value}")
async def on_task_start(task: AgentTask):
"""Called when a task starts executing."""
print(f"βΆοΈ Starting task: {task.command}")
async def on_task_complete(task: AgentTask, result: str):
"""Called when a task completes successfully."""
print(f"β
Task completed: {task.command}")
async def on_error(error: Exception):
"""Called when an error occurs."""
print(f"β Error: {error}")
# Attach callbacks
self.controller.on_state_change = on_state_change
self.controller.on_task_start = on_task_start
self.controller.on_task_complete = on_task_complete
self.controller.on_error = on_error
async def start(self):
"""Start the Beacon application."""
print("\n" + "="*BANNER_WIDTH)
print("π· BEACON - Voice-First Browser Controller")
print("="*BANNER_WIDTH)
# Welcome message
self.audio_handler.play_speech(
"Welcome to Beacon, your voice-first browser assistant!"
)
self.keyboard.start()
await self.controller.start()
while (self.audio_handler.is_playing):
await asyncio.sleep(0.1)
# Start keyboard handler
# Start controller
# Ready message
self.audio_handler.play_speech("Ready for your command.")
print("\n" + "="*BANNER_WIDTH)
print(f"Beacon is ready! Use {SHORTCUTS['voice']} to speak commands.")
print(f"Press {SHORTCUTS['stop_audio']} to stop audio playback.")
print(f"Press {SHORTCUTS['help']} for help.")
print(f"Press {SHORTCUTS['quit']} to quit.")
print("="*BANNER_WIDTH + "\n")
self.running = True
async def handle_help(self):
"""Handle help request."""
print("\n" + HELP_TEXT)
self.audio_handler.play_speech(HELP_TEXT)
async def handle_voice_command(self):
"""Handle voice command input."""
# Capture command
command = await self.voice.capture_command()
if not command:
return
# Check for built-in commands
command_lower = command.lower()
# Help commands
if command_lower in {"help", "help me", "what can i do", "commands"}:
await self.handle_help()
self.audio_handler.play_speech("Ready for your next command.")
return
# Exit commands
if command_lower in {"exit", "quit", "goodbye", "stop"}:
self.running = False
return
# Execute command with agent
try:
await self.controller.execute_command(command)
except Exception as exc:
print(f"β Command execution failed: {exc}")
async def run(self):
"""Main application loop."""
await self.start()
# Main event loop
while self.running:
# Check for keyboard events
event = self.keyboard.get_event(timeout=None)
if event == InputEvent.QUIT:
print("π Quit requested")
break
elif event == InputEvent.VOICE:
await self.handle_voice_command()
elif event == InputEvent.HELP:
await self.handle_help()
elif event == InputEvent.STOP_AUDIO:
self.voice.stop_audio()
# Small delay to prevent CPU spinning
await asyncio.sleep(0.1)
# Shutdown
await self.shutdown()
async def shutdown(self):
"""Gracefully shutdown the application."""
print("\nπ Shutting down Beacon...")
# Stop keyboard handler
self.keyboard.stop()
# Shutdown controller
await self.controller.shutdown()
# Goodbye message
self.audio_handler.play_speech("Shutting down. Goodbye!")
print("π Beacon closed.")
async def main():
"""Main entry point."""
app = BeaconApp()
await app.run()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n\nInterrupted by user.")
except Exception as exc:
print(f"\nβ Fatal error: {exc}")
raise