wakeword.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. @dataclass
  81. class FollowUpResponse(CommandMessage):
  82. """
  83. Antwort auf eine Rückfrage.
  84. Wird vom Satellite zum Server gesendet.
  85. """
  86. session_id: str = ""
  87. audio_data: str = "" # Hex-encodierte Audio-Antwort
  88. duration_seconds: float = 0.0
  89. follow_up_number: int = 0 # Welche Rückfrage wird beantwortet
  90. speech_detected: bool = True
  91. @dataclass
  92. class ConversationEnd(CommandMessage):
  93. """
  94. Konversation beenden.
  95. Kann vom Server oder Satellite gesendet werden.
  96. """
  97. session_id: str = ""
  98. reason: str = "" # "completed", "timeout", "cancelled", "error"
  99. final_response: str = "" # Letzte Antwort des Assistenten
  100. audio_data: str = "" # Hex-encodiertes TTS-Audio (optional)
  101. @dataclass
  102. class AssistantResponse(CommandMessage):
  103. """
  104. Assistenten-Antwort.
  105. Wird vom Server zum Satellite gesendet mit der Antwort.
  106. """
  107. session_id: str = ""
  108. text: str = "" # Antwort-Text
  109. audio_data: str = "" # Hex-encodiertes TTS-Audio
  110. is_final: bool = True # Ist dies die finale Antwort?
  111. has_follow_up: bool = False # Folgt eine Rückfrage?
  112. @dataclass
  113. class TranscriptionResult(CommandMessage):
  114. """
  115. Transkriptions-Ergebnis.
  116. Wird vom Server zum Satellite gesendet mit dem transkribierten Text.
  117. """
  118. session_id: str = ""
  119. text: str = "" # Transkribierter Text
  120. confidence: float = 0.0 # Transkriptions-Confidence
  121. language: str = "de" # Erkannte Sprache
  122. is_final: bool = True # Finale Transkription?
  123. @dataclass
  124. class IntentResult(CommandMessage):
  125. """
  126. Intent-Erkennungs-Ergebnis.
  127. Wird vom Server gesendet mit dem erkannten Intent.
  128. """
  129. session_id: str = ""
  130. intent: str = "" # Erkannter Intent
  131. confidence: float = 0.0
  132. entities: dict = field(default_factory=dict) # Extrahierte Entities
  133. handled_by: str = "" # Welches Plugin/Service handhabt den Intent
  134. @dataclass
  135. class TextInput(CommandMessage):
  136. """
  137. Text-Eingabe vom Client (Keyboard-Input).
  138. Wird als Alternative zu Spracheingabe verwendet.
  139. Startet eine neue Conversation oder setzt eine bestehende fort.
  140. Flow:
  141. 1. Client sendet TextInput an Server
  142. 2. Server erstellt/fortsetzt Conversation
  143. 3. Server emittiert speech_recognized Event
  144. 4. NLP verarbeitet Text
  145. 5. Bei Follow-Up: Server sendet FollowUpRequest zurück
  146. """
  147. satellite_id: str = ""
  148. text: str = "" # Eingegebener Text
  149. session_id: str = "" # Leer = neue Session, sonst Fortsetzung
  150. room_id: str = "" # Raum-ID
  151. language: str = "de" # Sprache
  152. is_follow_up: bool = False # Ist dies eine Antwort auf Follow-Up?
  153. follow_up_number: int = 0 # Welche Rückfrage wird beantwortet
  154. @dataclass
  155. class TextInputAck(CommandMessage):
  156. """
  157. Bestätigung für Text-Eingabe.
  158. Wird vom Server zum Client gesendet nach Empfang der Text-Eingabe.
  159. """
  160. session_id: str = "" # Zugewiesene Session-ID
  161. received: bool = True # Erfolgreich empfangen
  162. error: str = "" # Fehlermeldung falls nicht erfolgreich
  163. @dataclass
  164. class WakewordPause(CommandMessage):
  165. """Wakeword-Erkennung extern pausieren (Server → Satellite)."""
  166. reason: str = ""
  167. @dataclass
  168. class WakewordResume(CommandMessage):
  169. """Extern pausierte Wakeword-Erkennung fortsetzen (Server → Satellite)."""
  170. pass
  171. @dataclass
  172. class WakewordControlStop(CommandMessage):
  173. """Wakeword-Erkennung komplett stoppen (Server → Satellite)."""
  174. reason: str = ""
  175. @dataclass
  176. class WakewordControlStart(CommandMessage):
  177. """Wakeword-Erkennung starten (Server → Satellite)."""
  178. pass