basic.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. # -*- coding: utf-8 -*-
  2. """
  3. Grundlegende Event-Datenklassen.
  4. Definiert häufig verwendete Events im Trixy-System.
  5. """
  6. from dataclasses import dataclass, field
  7. from typing import Any
  8. from trixy_core.events.event_data.base import EventData
  9. @dataclass
  10. class SatelliteConnected(EventData):
  11. """Event wenn ein Satellite sich verbindet."""
  12. satellite_id: str = ""
  13. room: str = ""
  14. alias: str = ""
  15. mac_address: str = ""
  16. ip_address: str = ""
  17. @dataclass
  18. class SatelliteDisconnected(EventData):
  19. """Event wenn ein Satellite die Verbindung trennt."""
  20. satellite_id: str = ""
  21. room: str = ""
  22. alias: str = ""
  23. reason: str = ""
  24. @dataclass
  25. class MusicSocketConnected(EventData):
  26. """Event wenn der Music-Socket eines Satellites verbunden ist."""
  27. satellite_id: str = ""
  28. alias: str = ""
  29. room: str = ""
  30. @dataclass
  31. class WakewordReceived(EventData):
  32. """Event wenn ein Wakeword erkannt wurde."""
  33. satellite_id: str = ""
  34. wakeword_model: str = ""
  35. confidence: float = 0.0
  36. audio_level: float = 0.0
  37. @dataclass
  38. class RawAudioInputReceived(EventData):
  39. """Event wenn rohe Audiodaten empfangen wurden."""
  40. satellite_id: str = ""
  41. audio_data: bytes = field(default=b"", repr=False)
  42. sample_rate: int = 16000
  43. duration_seconds: float = 0.0
  44. @dataclass
  45. class SpeechRecognized(EventData):
  46. """
  47. Event wenn Sprache erkannt wurde (STT-Ergebnis).
  48. Attributes:
  49. satellite_id: ID des Satellites
  50. room_id: Raum-ID des Satellites
  51. text: Erkannter Text
  52. confidence: Konfidenz der Erkennung (0.0-1.0)
  53. language: Sprache des Textes
  54. is_final: Ist dies das finale Ergebnis? (vs. Zwischen-Ergebnis)
  55. source: Quelle der Eingabe ("stt", "keyboard", etc.)
  56. alternatives: Alternative Erkennungen
  57. """
  58. satellite_id: str = ""
  59. room_id: str = ""
  60. text: str = ""
  61. confidence: float = 0.0
  62. language: str = "de"
  63. is_final: bool = True
  64. source: str = "stt" # "stt", "keyboard", "api"
  65. alternatives: list[str] = field(default_factory=list)
  66. @dataclass
  67. class IntentReceived(EventData):
  68. """
  69. Event wenn ein Intent erkannt wurde.
  70. Wird vom NLP-Provider emittiert nachdem Text verarbeitet wurde.
  71. Intent-Handler Plugins reagieren auf dieses Event.
  72. Flow:
  73. 1. NLP emittiert IntentReceived
  74. 2. Intent-Handler Plugin verarbeitet und emittiert IntentHandled
  75. 3. Falls keine Antwort: CreateOutputText wird emittiert
  76. 4. Response Generator erstellt Text und emittiert OutputTextCreated
  77. 5. TTS Plugin reagiert auf OutputTextCreated
  78. """
  79. satellite_id: str = ""
  80. intent: str = ""
  81. confidence: float = 0.0
  82. slots: dict[str, Any] = field(default_factory=dict)
  83. original_text: str = ""
  84. session_id: str = ""
  85. room_id: str = ""
  86. wakeword_type: str = "custom" # "custom" oder "system_command"
  87. is_authenticated: bool = False # Admin-Authentifizierung
  88. language: str = "de"
  89. metadata: dict[str, Any] = field(default_factory=dict)
  90. @dataclass
  91. class IntentHandled(EventData):
  92. """
  93. Event wenn ein Intent-Handler die Verarbeitung abgeschlossen hat.
  94. Wird vom Intent-Handler emittiert nach der Ausführung.
  95. Enthält das Ergebnis der Aktion.
  96. Attributes:
  97. satellite_id: ID des Satellites
  98. session_id: Konversations-Session-ID
  99. intent: Verarbeiteter Intent
  100. success: War die Verarbeitung erfolgreich?
  101. response_text: Optionaler Antworttext (falls Handler selbst antwortet)
  102. data: Ergebnisdaten der Aktion
  103. error: Fehlermeldung falls nicht erfolgreich
  104. needs_followup: Erwartet Rückfrage/Fortsetzung?
  105. suppress_response: Keine Antwort generieren?
  106. """
  107. satellite_id: str = ""
  108. session_id: str = ""
  109. room_id: str = ""
  110. intent: str = ""
  111. original_text: str = ""
  112. slots: dict[str, Any] = field(default_factory=dict)
  113. success: bool = True
  114. response_text: str = "" # Falls Handler eigene Antwort liefert
  115. data: dict[str, Any] = field(default_factory=dict) # Ergebnisdaten
  116. error: str = ""
  117. needs_followup: bool = False # Konversation fortsetzen?
  118. followup_prompt: str = "" # Rückfrage-Text
  119. suppress_response: bool = False # Keine Antwort generieren
  120. @dataclass
  121. class ConversationStarted(EventData):
  122. """Event wenn eine Konversation beginnt."""
  123. satellite_id: str = ""
  124. conversation_id: str = ""
  125. @dataclass
  126. class ConversationEnded(EventData):
  127. """Event wenn eine Konversation endet."""
  128. satellite_id: str = ""
  129. conversation_id: str = ""
  130. reason: str = "" # timeout, user_ended, error, completed
  131. @dataclass
  132. class PluginLoaded(EventData):
  133. """Event wenn ein Plugin geladen wurde."""
  134. plugin_name: str = ""
  135. plugin_version: str = ""
  136. @dataclass
  137. class PluginUnloaded(EventData):
  138. """Event wenn ein Plugin entladen wurde."""
  139. plugin_name: str = ""
  140. reason: str = ""
  141. @dataclass
  142. class SystemShutdown(EventData):
  143. """Event vor dem Herunterfahren des Systems."""
  144. reason: str = ""
  145. force: bool = False
  146. @dataclass
  147. class TTSRequest(EventData):
  148. """Event für TTS-Anfrage."""
  149. request_id: str = ""
  150. satellite_id: str = ""
  151. text: str = ""
  152. voice: str = "default"
  153. speed: float = 1.0
  154. volume: float = 1.0
  155. @dataclass
  156. class TTSCompleted(EventData):
  157. """Event wenn TTS abgeschlossen ist."""
  158. request_id: str = ""
  159. satellite_id: str = ""
  160. success: bool = True
  161. audio_duration: float = 0.0
  162. error: str = ""
  163. @dataclass
  164. class StreamStart(EventData):
  165. """Event zum Starten eines Audio-Streams."""
  166. satellite_id: str = ""
  167. source: str = ""
  168. volume: float = 1.0
  169. stream_type: str = "music" # "music", "audio", "tts"
  170. @dataclass
  171. class StreamStop(EventData):
  172. """Event zum Stoppen eines Audio-Streams."""
  173. satellite_id: str = ""
  174. stream_type: str = "all" # "music", "audio", "tts", "all"
  175. @dataclass
  176. class ProcessingResult(EventData):
  177. """Event mit Verarbeitungsergebnis (für Standalone-Modus)."""
  178. session_id: str = ""
  179. success: bool = True
  180. text: str = ""
  181. intent: str = ""
  182. response_text: str = ""
  183. error: str = ""
  184. @dataclass
  185. class CreateOutputText(EventData):
  186. """
  187. Event zur Generierung von Antworttext.
  188. Wird emittiert nach IntentHandled wenn:
  189. - Handler keine response_text geliefert hat
  190. - suppress_response nicht gesetzt ist
  191. Response-Generator Plugin reagiert und emittiert OutputTextCreated.
  192. Attributes:
  193. satellite_id: ID des Satellites
  194. session_id: Konversations-Session-ID
  195. room_id: Raum-ID
  196. intent: Erkannter Intent
  197. original_text: Ursprüngliche Benutzeranfrage
  198. slots: Extrahierte Slot-Werte
  199. handler_data: Ergebnisdaten des Intent-Handlers
  200. handler_success: War der Handler erfolgreich?
  201. handler_error: Fehlermeldung des Handlers
  202. language: Sprache für Antwort
  203. """
  204. satellite_id: str = ""
  205. session_id: str = ""
  206. room_id: str = ""
  207. intent: str = ""
  208. original_text: str = ""
  209. slots: dict[str, Any] = field(default_factory=dict)
  210. handler_data: dict[str, Any] = field(default_factory=dict)
  211. handler_success: bool = True
  212. handler_error: str = ""
  213. language: str = "de"
  214. @dataclass
  215. class OutputTextCreated(EventData):
  216. """
  217. Event wenn Antworttext generiert wurde.
  218. Wird vom Response-Generator emittiert.
  219. TTS Plugin reagiert darauf und spricht den Text.
  220. Conversation Service aktualisiert die Session.
  221. Attributes:
  222. satellite_id: ID des Satellites
  223. session_id: Konversations-Session-ID
  224. room_id: Raum-ID
  225. text: Generierter Antworttext
  226. intent: Ursprünglicher Intent
  227. is_followup: Ist dies eine Rückfrage?
  228. expects_response: Erwartet Benutzerantwort?
  229. """
  230. satellite_id: str = ""
  231. session_id: str = ""
  232. room_id: str = ""
  233. text: str = ""
  234. intent: str = ""
  235. is_followup: bool = False
  236. expects_response: bool = False
  237. @dataclass
  238. class FollowUpExpected(EventData):
  239. """
  240. Event wenn eine Rückfrage erwartet wird.
  241. Wird emittiert nachdem TTS abgeschlossen ist und der Benutzer
  242. antworten soll. Der Client soll ohne Wakeword-Erkennung
  243. direkt Audio streamen.
  244. Flow:
  245. 1. Handler setzt needs_followup=True
  246. 2. IntentDispatcher emittiert output_text_created mit expects_response=True
  247. 3. TTS spricht den Text
  248. 4. Nach TTS-Abschluss: followup_expected wird emittiert
  249. 5. Client wechselt in Follow-up-Modus (direktes STT ohne Wakeword)
  250. 6. Client streamt Audio bis Stille/Timeout
  251. 7. Server verarbeitet Audio → speech_recognized → NLP → ...
  252. Attributes:
  253. satellite_id: ID des Satellites
  254. session_id: Konversations-Session-ID
  255. room_id: Raum-ID
  256. timeout_seconds: Maximale Wartezeit auf Antwort
  257. followup_context: Kontext für die erwartete Antwort (Intent, Slots)
  258. """
  259. satellite_id: str = ""
  260. session_id: str = ""
  261. room_id: str = ""
  262. timeout_seconds: float = 30.0
  263. followup_context: dict[str, Any] = field(default_factory=dict)
  264. @dataclass
  265. class SatelliteRecordingStarted(EventData):
  266. """Event wenn server-gesteuerte Aufnahme gestartet wurde."""
  267. satellite_id: str = ""
  268. recording_id: str = ""
  269. timeout_seconds: float = 300.0
  270. @dataclass
  271. class SatelliteRecordingStopped(EventData):
  272. """Event wenn server-gesteuerte Aufnahme beendet wurde."""
  273. satellite_id: str = ""
  274. recording_id: str = ""
  275. reason: str = ""
  276. duration_seconds: float = 0.0
  277. audio_size_bytes: int = 0
  278. filename: str = ""
  279. @dataclass
  280. class SatelliteRecordingCleared(EventData):
  281. """Event wenn der Recording-Buffer geleert wurde."""
  282. satellite_id: str = ""
  283. recording_id: str = ""
  284. cleared_bytes: int = 0
  285. @dataclass
  286. class SatelliteStreamStarted(EventData):
  287. """Event wenn Live-Audio-Stream gestartet wurde."""
  288. satellite_id: str = ""
  289. target_satellite_id: str = ""
  290. stream_id: str = ""
  291. @dataclass
  292. class SatelliteStreamStopped(EventData):
  293. """Event wenn Live-Audio-Stream beendet wurde."""
  294. satellite_id: str = ""
  295. target_satellite_id: str = ""
  296. stream_id: str = ""
  297. reason: str = ""
  298. duration_seconds: float = 0.0
  299. bytes_forwarded: int = 0
  300. @dataclass
  301. class SatelliteStreamRecordStarted(EventData):
  302. """Event wenn Stream-Aufnahme in Datei gestartet wurde."""
  303. satellite_id: str = ""
  304. stream_id: str = ""
  305. filename: str = ""
  306. @dataclass
  307. class SatelliteStreamRecordStopped(EventData):
  308. """Event wenn Stream-Aufnahme beendet wurde."""
  309. satellite_id: str = ""
  310. stream_id: str = ""
  311. filename: str = ""
  312. reason: str = ""
  313. duration_seconds: float = 0.0
  314. file_size_bytes: int = 0