# Event Handler System API Documentation ## Overview The Event Handler System is the **central communication hub** of the Trixy voice assistant. It implements a sophisticated event-driven architecture that allows all components to communicate asynchronously through events. **Location**: `./trixy_core/events/` ## Core Components ### EventHandler Main event processing and management class. ### @TrixyEvent Decorator Decorator for automatic event handler registration. ### Event Data Classes Structured data containers for all event types. ## API Reference ### EventHandler Class #### Initialization ```python from trixy_core.events import EventHandler event_handler = EventHandler( max_history=1000, # Maximum event history entries enable_debug=True, # Enable debug logging enable_filtering=True, # Allow event filtering max_concurrent_handlers=10 # Max parallel event handlers ) ``` #### Core Methods ##### `trigger_event(event_type, **kwargs)` Triggers an event with optional data. ```python from trixy_core.events import EventType # Trigger system startup event_handler.trigger_event(EventType.SYSTEM_STARTUP) # Trigger wakeword with data event_handler.trigger_event( EventType.WAKEWORD_RECEIVED, wakeword_id="trixy", speaker_info=speaker_info, satellite_info=satellite_info, volume=0.8 ) ``` ##### `register_handler_object(obj)` Registers all @TrixyEvent decorated methods from an object. ```python class MyPlugin: @TrixyEvent(["wakeword_received", "text_received"]) def handle_events(self, event_name, event_data): pass plugin = MyPlugin() event_handler.register_handler_object(plugin) ``` ##### `unregister_handler_object(obj)` Removes all event handlers from an object. ```python event_handler.unregister_handler_object(plugin) ``` ##### `get_event_history(limit=None, event_types=None)` Retrieves event history with optional filtering. ```python # Get last 10 events recent_events = event_handler.get_event_history(limit=10) # Get only wakeword events wakeword_events = event_handler.get_event_history( event_types=[EventType.WAKEWORD_RECEIVED] ) ``` ##### `get_statistics()` Returns comprehensive event system statistics. ```python stats = event_handler.get_statistics() print(f"Total events: {stats['total_events_triggered']}") print(f"Registered handlers: {stats['total_handlers_registered']}") ``` ### @TrixyEvent Decorator #### Usage ```python from trixy_core.events import TrixyEvent class VoicePlugin: @TrixyEvent(["wakeword_received"]) def on_wakeword(self, event_name, event_data): print(f"Wakeword detected: {event_data.wakeword_id}") @TrixyEvent(["text_received", "intent_received"], priority=5) def on_text_or_intent(self, event_name, event_data): if event_name == "text_received": print(f"Text: {event_data.text}") elif event_name == "intent_received": print(f"Intent: {event_data.intent}") ``` #### Parameters - `events`: List of event names to listen for - `priority`: Handler priority (lower numbers = higher priority) ## Event Types ### Satellite Management Events - `SATELLITE_CONNECTED`: When satellite establishes connection - `SATELLITE_DISCONNECTED`: When satellite loses connection - `SATELLITE_REGISTERED`: When new satellite is registered ### Wakeword & Audio Events - `WAKEWORD_RECEIVED`: When satellite detects wakeword - `RAW_AUDIO_INPUT_RECEIVED`: When audio recording completes ### Processing Events - `TEXT_RECEIVED`: When STT converts audio to text - `INTENT_RECEIVED`: When NLP extracts intent from text - `TTS_RECEIVED`: When TTS generates response audio ### System Events - `SYSTEM_STARTUP`: System initialization complete - `SYSTEM_SHUTDOWN`: System shutting down - `PLUGIN_LOADED`: Plugin successfully loaded - `PLUGIN_UNLOADED`: Plugin unloaded ### Conversation Events - `CONVERSATION_STARTED`: New conversation session started - `CONVERSATION_ENDED`: Conversation session ended - `CONVERSATION_STATE_CHANGED`: Conversation state transition ### Arbitration Events - `ARBITRATION_STARTED`: Multi-satellite arbitration begins - `ARBITRATION_COMPLETED`: Satellite selection completed - `SATELLITE_SELECTED`: Specific satellite chosen - `SATELLITE_IGNORED`: Satellite not selected ## Event Data Structures ### WakewordReceivedEventData ```python @dataclass class WakewordReceivedEventData: wakeword_id: str # "trixy" or "system_command" speaker_info: SpeakerInfo # Speaker identification satellite_info: SatelliteInfo # Source satellite info volume: float # Audio volume level confidence: float = 1.0 # Detection confidence timestamp: datetime = field(default_factory=datetime.now) ``` ### TextReceivedEventData ```python @dataclass class TextReceivedEventData: conversation_id: str # Conversation session ID text: str # Recognized text confidence: float # Recognition confidence speaker_info: SpeakerInfo # Speaker identification language: str = "en" # Detected language timestamp: datetime = field(default_factory=datetime.now) ``` ### IntentReceivedEventData ```python @dataclass class IntentReceivedEventData: conversation_id: str # Conversation session ID intent: str # Recognized intent entities: Dict[str, Any] # Extracted entities confidence: float # Intent confidence original_text: str # Source text timestamp: datetime = field(default_factory=datetime.now) ``` ## Integration Examples ### Plugin Integration ```python from trixy_core.plugins import TrixyPlugin from trixy_core.events import TrixyEvent, EventType class WeatherPlugin(TrixyPlugin): @TrixyEvent(["intent_received"]) def handle_weather_intent(self, event_name, event_data): if event_data.intent == "get_weather": location = event_data.entities.get("location", "default") weather_info = self.get_weather(location) # Trigger TTS response self.application.get_event_handler().trigger_event( EventType.TTS_RECEIVED, conversation_id=event_data.conversation_id, text=f"The weather in {location} is {weather_info}", voice_settings={"speed": 1.0, "pitch": 1.0} ) ``` ### Satellite Communication ```python from trixy_core.events import TrixyEvent class SatelliteHandler: @TrixyEvent(["satellite_connected"]) def on_satellite_connected(self, event_name, event_data): satellite = event_data.satellite_info print(f"Satellite {satellite.alias} connected from {satellite.room_id}") # Send welcome message satellite_manager = self.application.get_satellite_manager() satellite_manager.say( satellite.satellite_id, "Welcome! I'm ready to assist you." ) ``` ## Configuration ### EventHandler Configuration ```python event_config = { "max_history": 1000, # Event history limit "enable_debug": True, # Debug logging "enable_filtering": True, # Event filtering "max_concurrent_handlers": 10, # Parallel handlers "enable_async_processing": True, # Async event processing "handler_timeout": 30.0, # Handler timeout seconds "enable_event_persistence": False # Persist events to disk } ``` ## Error Handling ### Event Handler Errors ```python try: event_handler.trigger_event(EventType.WAKEWORD_RECEIVED, invalid_param=True) except EventValidationError as e: print(f"Invalid event data: {e}") except EventHandlerError as e: print(f"Handler error: {e}") ``` ### Handler Registration Errors ```python try: event_handler.register_handler_object(invalid_object) except HandlerRegistrationError as e: print(f"Registration failed: {e}") ``` ## Best Practices ### 1. Event Handler Design ```python class GoodEventHandler: @TrixyEvent(["specific_event"], priority=10) def handle_specific_event(self, event_name, event_data): # Keep handlers focused and fast try: self.process_quickly(event_data) except Exception as e: self.logger.error(f"Handler error: {e}") # Don't let exceptions propagate ``` ### 2. Event Data Validation ```python @TrixyEvent(["wakeword_received"]) def on_wakeword(self, event_name, event_data): # Always validate event data if not event_data.wakeword_id: return if event_data.confidence < 0.5: return # Ignore low-confidence detections self.process_wakeword(event_data) ``` ### 3. Async Processing for Heavy Work ```python import asyncio @TrixyEvent(["raw_audio_input_received"]) def on_audio_input(self, event_name, event_data): # Don't block the event loop asyncio.create_task(self.process_audio_async(event_data)) async def process_audio_async(self, event_data): # Heavy processing here result = await self.transcribe_audio(event_data.audio_data) # Trigger follow-up event self.application.get_event_handler().trigger_event( EventType.TEXT_RECEIVED, conversation_id=event_data.conversation_id, text=result.text, confidence=result.confidence ) ``` ## Performance Considerations - **Handler Speed**: Keep event handlers fast (< 100ms) - **Memory Usage**: Event history is limited by `max_history` - **Concurrency**: Use `max_concurrent_handlers` to control parallelism - **Filtering**: Enable filtering to reduce unnecessary handler calls ## Thread Safety The EventHandler is fully thread-safe and can be used concurrently from multiple threads. All operations use appropriate locking mechanisms. ## Debugging ### Enable Debug Mode ```python event_handler = EventHandler(enable_debug=True) ``` ### Event History Analysis ```python # Analyze event patterns history = event_handler.get_event_history(limit=100) event_types = [event.event_type for event in history] most_common = Counter(event_types).most_common(5) print(f"Most common events: {most_common}") ``` ### Statistics Monitoring ```python stats = event_handler.get_statistics() if stats['failed_handlers'] > 0: print(f"Warning: {stats['failed_handlers']} handler failures detected") ```