Event-Driven Core
At the heart of the Orchestrator is the EventBus. This internal mechanism allows for decoupled communication between "Sources" (which produce events) and "Managers" (which consume and act on events).
The Event Bus
The EventBus is an asynchronous implementation that supports:
- Subscribe: Register an async callback for a specific EventType.
- Publish: Broadcast an Event to all registered subscribers.
- Concurrency: Callbacks are executed as independent tasks, ensuring that a slow subscriber (like a logger) doesn't block the main interaction flow.
Common Event Types
The system defines a standard set of events in orchestrator/events.py:
| Event Category | Type | Description |
|---|---|---|
| Input | input.speech_start |
User started speaking (useful for interruption). |
input.transcript.final |
Finalized sentence from STT. | |
input.transcript.interim |
Real-time partial transcript from STT. | |
| LLM | llm.request |
Prompt sent to the LLM. |
llm.token |
A single token generated by the LLM. | |
llm.response_done |
LLM generation is complete. | |
llm.cancelled |
LLM generation was aborted. | |
| TTS | tts.request |
Text sent to the TTS service for synthesis. |
tts.audio_chunk |
Raw PCM audio received from TTS. | |
| System | system.state_changed |
Updates to listening, responding, or playing states. |
Why Event-Driven?
- Low Latency: As soon as a "Source" gets data (like a finalized transcript), it fires an event. The
InteractionManagerstarts the LLM request immediately while other components (like the UI) update simultaneously. - Extensibility: Want to add OBS subtitles? Simply create a
SubtitleManagerthat subscribes totts.requestorllm.token. You don't need to modify the core interaction logic. - Robustness: If one subscriber fails, the
EventBuscatches the exception and continues notifying others.