wakeword.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. # -*- coding: utf-8 -*-
  2. """
  3. Wakeword-bezogene Netzwerk-Befehle.
  4. """
  5. from dataclasses import dataclass, field
  6. from datetime import datetime
  7. from trixy_core.network.cmd.base import CommandMessage
  8. @dataclass
  9. class WakewordDetected(CommandMessage):
  10. """
  11. Wakeword-Erkennung durch einen Satellite.
  12. Wird vom Satellite zum Server gesendet, wenn ein Wakeword erkannt wurde.
  13. Der Server führt dann die Arbitrierung durch (höchstes Audio-Level gewinnt).
  14. Enthält auch die Audio-Chunks mit dem Wakeword für Stimmenerkennung.
  15. """
  16. satellite_id: str = ""
  17. wakeword_type: str = "" # "custom" oder "system_command"
  18. wakeword_model: str = "" # Modell-Name (z.B. "hey_jarvis")
  19. confidence: float = 0.0 # Erkennungs-Confidence
  20. audio_level: float = 0.0 # RMS-Level für Arbitration
  21. audio_chunks: list[str] = field(default_factory=list) # Hex-encodierte Chunks
  22. timestamp: str = "" # ISO-Format Timestamp
  23. client_session_id: str = "" # Vom Client generierte Session-ID
  24. @dataclass
  25. class WakewordSelected(CommandMessage):
  26. """
  27. Satellite wurde für die Konversation ausgewählt.
  28. Wird vom Server zum ausgewählten Satellite gesendet.
  29. Enthält die Server-generierte Session-ID.
  30. """
  31. session_id: str = "" # Server-generierte Session-ID
  32. conversation_id: str = "" # Konversations-ID (= session_id)
  33. start_streaming: bool = True # Audio-Streaming starten
  34. max_duration_seconds: float = 60.0 # Max. Aufnahmedauer
  35. silence_timeout_seconds: float = 3.0 # Stille-Timeout
  36. @dataclass
  37. class WakewordAbort(CommandMessage):
  38. """
  39. Wakeword-Erkennung abbrechen.
  40. Wird gesendet, wenn ein anderer Satellite ausgewählt wurde
  41. oder die Erkennung abgebrochen werden soll.
  42. """
  43. reason: str = ""
  44. resume_detection: bool = True
  45. selected_satellite_id: str = "" # Welcher Satellite wurde stattdessen ausgewählt
  46. @dataclass
  47. class RecordingComplete(CommandMessage):
  48. """
  49. Audio-Aufnahme abgeschlossen.
  50. Wird vom Satellite zum Server gesendet, wenn die Aufnahme
  51. beendet wurde (durch Stille oder Timeout).
  52. """
  53. session_id: str = ""
  54. audio_data: str = "" # Hex-encodierte Audio-Daten
  55. duration_seconds: float = 0.0
  56. ended_by: str = "" # "silence", "timeout", "user", "error"
  57. speech_detected: bool = True # Wurde Sprache erkannt?
  58. peak_level: float = 0.0 # Peak Audio-Level
  59. vad_stats: dict = field(default_factory=dict) # VAD-Statistiken
  60. @dataclass
  61. class RecordingAck(CommandMessage):
  62. """
  63. Bestätigung für empfangene Aufnahme.
  64. Wird vom Server zum Satellite gesendet nach Empfang der Aufnahme.
  65. """
  66. session_id: str = ""
  67. received_bytes: int = 0
  68. processing: bool = True # Wird verarbeitet
  69. @dataclass
  70. class FollowUpRequest(CommandMessage):
  71. """
  72. Rückfrage vom Server an den Satellite.
  73. Der Assistent stellt eine Rückfrage und erwartet eine Antwort.
  74. """
  75. session_id: str = ""
  76. question: str = "" # Rückfrage-Text
  77. audio_data: str = "" # Hex-encodiertes TTS-Audio (optional)
  78. follow_up_number: int = 0 # Nummer der Rückfrage
  79. timeout_seconds: float = 60.0 # Timeout für Antwort
  80. audio_duration: float = 0.0 # TTS-Audio-Dauer in Sekunden (fuer Wait)
  81. @dataclass
  82. class FollowUpResponse(CommandMessage):
  83. """
  84. Antwort auf eine Rückfrage.
  85. Wird vom Satellite zum Server gesendet.
  86. """
  87. session_id: str = ""
  88. audio_data: str = "" # Hex-encodierte Audio-Antwort
  89. duration_seconds: float = 0.0
  90. follow_up_number: int = 0 # Welche Rückfrage wird beantwortet
  91. speech_detected: bool = True
  92. @dataclass
  93. class ConversationEnd(CommandMessage):
  94. """
  95. Konversation beenden.
  96. Kann vom Server oder Satellite gesendet werden.
  97. """
  98. session_id: str = ""
  99. reason: str = "" # "completed", "timeout", "cancelled", "error"
  100. final_response: str = "" # Letzte Antwort des Assistenten
  101. audio_data: str = "" # Hex-encodiertes TTS-Audio (optional)
  102. @dataclass
  103. class AssistantResponse(CommandMessage):
  104. """
  105. Assistenten-Antwort.
  106. Wird vom Server zum Satellite gesendet mit der Antwort.
  107. """
  108. session_id: str = ""
  109. text: str = "" # Antwort-Text
  110. audio_data: str = "" # Hex-encodiertes TTS-Audio
  111. is_final: bool = True # Ist dies die finale Antwort?
  112. has_follow_up: bool = False # Folgt eine Rückfrage?
  113. @dataclass
  114. class TranscriptionResult(CommandMessage):
  115. """
  116. Transkriptions-Ergebnis.
  117. Wird vom Server zum Satellite gesendet mit dem transkribierten Text.
  118. """
  119. session_id: str = ""
  120. text: str = "" # Transkribierter Text
  121. confidence: float = 0.0 # Transkriptions-Confidence
  122. language: str = "de" # Erkannte Sprache
  123. is_final: bool = True # Finale Transkription?
  124. @dataclass
  125. class IntentResult(CommandMessage):
  126. """
  127. Intent-Erkennungs-Ergebnis.
  128. Wird vom Server gesendet mit dem erkannten Intent.
  129. """
  130. session_id: str = ""
  131. intent: str = "" # Erkannter Intent
  132. confidence: float = 0.0
  133. entities: dict = field(default_factory=dict) # Extrahierte Entities
  134. handled_by: str = "" # Welches Plugin/Service handhabt den Intent
  135. @dataclass
  136. class TextInput(CommandMessage):
  137. """
  138. Text-Eingabe vom Client (Keyboard-Input).
  139. Wird als Alternative zu Spracheingabe verwendet.
  140. Startet eine neue Conversation oder setzt eine bestehende fort.
  141. Flow:
  142. 1. Client sendet TextInput an Server
  143. 2. Server erstellt/fortsetzt Conversation
  144. 3. Server emittiert speech_recognized Event
  145. 4. NLP verarbeitet Text
  146. 5. Bei Follow-Up: Server sendet FollowUpRequest zurück
  147. """
  148. satellite_id: str = ""
  149. text: str = "" # Eingegebener Text
  150. session_id: str = "" # Leer = neue Session, sonst Fortsetzung
  151. room_id: str = "" # Raum-ID
  152. language: str = "de" # Sprache
  153. is_follow_up: bool = False # Ist dies eine Antwort auf Follow-Up?
  154. follow_up_number: int = 0 # Welche Rückfrage wird beantwortet
  155. @dataclass
  156. class TextInputAck(CommandMessage):
  157. """
  158. Bestätigung für Text-Eingabe.
  159. Wird vom Server zum Client gesendet nach Empfang der Text-Eingabe.
  160. """
  161. session_id: str = "" # Zugewiesene Session-ID
  162. received: bool = True # Erfolgreich empfangen
  163. error: str = "" # Fehlermeldung falls nicht erfolgreich
  164. @dataclass
  165. class WakewordPause(CommandMessage):
  166. """Wakeword-Erkennung extern pausieren (Server → Satellite)."""
  167. reason: str = ""
  168. @dataclass
  169. class WakewordResume(CommandMessage):
  170. """Extern pausierte Wakeword-Erkennung fortsetzen (Server → Satellite)."""
  171. pass
  172. @dataclass
  173. class WakewordControlStop(CommandMessage):
  174. """Wakeword-Erkennung komplett stoppen (Server → Satellite)."""
  175. reason: str = ""
  176. @dataclass
  177. class WakewordControlStart(CommandMessage):
  178. """Wakeword-Erkennung starten (Server → Satellite)."""
  179. pass