system_intents.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. # -*- coding: utf-8 -*-
  2. """
  3. System-Intents für Trixy.
  4. Hardkodierte System-Intents die immer verfügbar sind,
  5. unabhängig von geladenen Plugins.
  6. Sicherheitsstufen:
  7. - PUBLIC: Für alle verfügbar
  8. - ADMIN: Nur mit "system_command" Wakeword und Login
  9. """
  10. from enum import Enum, auto
  11. from dataclasses import dataclass, field
  12. from typing import Any
  13. class IntentSecurityLevel(Enum):
  14. """Sicherheitsstufe eines Intents."""
  15. PUBLIC = auto() # Für alle verfügbar
  16. ADMIN = auto() # Erfordert system_command Wakeword + Login
  17. @dataclass
  18. class SystemIntentInfo:
  19. """
  20. Definition eines System-Intents.
  21. Attributes:
  22. name: Intent-Name
  23. description: Beschreibung
  24. security: Sicherheitsstufe
  25. slots: Slot-Definitionen
  26. examples: Beispiel-Phrasen
  27. """
  28. name: str
  29. description: str
  30. security: IntentSecurityLevel = IntentSecurityLevel.PUBLIC
  31. slots: dict[str, dict[str, Any]] = field(default_factory=dict)
  32. examples: list[str] = field(default_factory=list)
  33. patterns: list[str] = field(default_factory=list)
  34. def to_dict(self) -> dict[str, Any]:
  35. """Konvertiert zu Dictionary für NLP-Prompt."""
  36. result = {
  37. "name": self.name,
  38. "description": self.description,
  39. "slots": self.slots,
  40. "examples": self.examples,
  41. "plugin": "_system",
  42. "security": self.security.name.lower(),
  43. }
  44. if self.patterns:
  45. result["patterns"] = self.patterns
  46. return result
  47. # =============================================================================
  48. # Öffentliche System-Intents (PUBLIC)
  49. # =============================================================================
  50. PUBLIC_SYSTEM_INTENTS: list[SystemIntentInfo] = [
  51. SystemIntentInfo(
  52. name="health_check",
  53. description="Systemstatus und Wohlbefinden abfragen",
  54. examples=["Wie geht es dir?", "Alles okay?", "Bist du da?", "Status"],
  55. patterns=[
  56. "(wie geht|wie gehts) [es] [dir]",
  57. "alles (okay|ok|klar)",
  58. "bist [du] da",
  59. ],
  60. ),
  61. SystemIntentInfo(
  62. name="get_time",
  63. description="Aktuelle Uhrzeit abfragen",
  64. examples=["Wie spät ist es?", "Uhrzeit", "Wie viel Uhr ist es?"],
  65. patterns=[
  66. "wie (spaet|viel uhr) ist [es]",
  67. "uhrzeit",
  68. ],
  69. ),
  70. SystemIntentInfo(
  71. name="get_date",
  72. description="Aktuelles Datum abfragen",
  73. examples=["Welcher Tag ist heute?", "Datum", "Welches Datum haben wir?"],
  74. patterns=[
  75. "welcher tag ist [heute]",
  76. "welches datum (haben wir|ist heute)",
  77. "datum",
  78. ],
  79. ),
  80. SystemIntentInfo(
  81. name="help",
  82. description="Hilfe und verfügbare Befehle anzeigen",
  83. examples=["Hilfe", "Was kannst du?", "Befehle"],
  84. patterns=[
  85. "hilfe",
  86. "was kannst [du]",
  87. "befehle",
  88. ],
  89. ),
  90. SystemIntentInfo(
  91. name="cancel",
  92. description="Aktuelle Aktion abbrechen",
  93. examples=["Abbrechen", "Vergiss es", "Egal"],
  94. patterns=["abbrechen", "vergiss [es]", "egal"],
  95. ),
  96. SystemIntentInfo(
  97. name="repeat",
  98. description="Letzte Antwort wiederholen",
  99. examples=["Wiederhole das", "Nochmal", "Was hast du gesagt?"],
  100. patterns=[
  101. "wiederhole [das]",
  102. "nochmal",
  103. "was hast [du] gesagt",
  104. ],
  105. ),
  106. SystemIntentInfo(
  107. name="stop",
  108. description="Stoppt alle aktiven Medien (Musik, TTS, Streams)",
  109. examples=["Stopp", "Halt", "Alles stoppen", "Sei still", "Ruhe"],
  110. patterns=["stopp", "halt", "alles stoppen", "sei still", "ruhe"],
  111. ),
  112. SystemIntentInfo(
  113. name="pause",
  114. description="Pausiert die aktuelle Wiedergabe",
  115. examples=["Pause", "Pausieren"],
  116. patterns=["pause", "pausieren"],
  117. ),
  118. SystemIntentInfo(
  119. name="resume",
  120. description="Setzt die pausierte Wiedergabe fort",
  121. examples=["Weiterspielen", "Fortsetzen", "Weiter machen", "Play"],
  122. patterns=[
  123. "weiterspielen",
  124. "fortsetzen",
  125. "weiter [machen]",
  126. "play",
  127. ],
  128. ),
  129. SystemIntentInfo(
  130. name="volume_up",
  131. description="Lautstärke erhöhen",
  132. slots={"amount": {"type": "int", "description": "Prozent (optional)"}},
  133. examples=["Lauter", "Lautstärke hoch", "Mach lauter"],
  134. patterns=[
  135. "lauter",
  136. "lautstaerke (hoch|rauf)",
  137. "(mach|dreh) [es] lauter",
  138. ],
  139. ),
  140. SystemIntentInfo(
  141. name="volume_down",
  142. description="Lautstärke verringern",
  143. slots={"amount": {"type": "int", "description": "Prozent (optional)"}},
  144. examples=["Leiser", "Lautstärke runter", "Mach leiser"],
  145. patterns=[
  146. "leiser",
  147. "lautstaerke (runter|rauf)",
  148. "(mach|dreh) [es] leiser",
  149. ],
  150. ),
  151. SystemIntentInfo(
  152. name="volume_set",
  153. description="Lautstärke auf bestimmten Wert setzen",
  154. slots={"level": {"type": "int", "required": True, "description": "Prozent 0-100"}},
  155. examples=["Lautstärke auf 50", "Setze Lautstärke auf 30 Prozent"],
  156. patterns=[
  157. "lautstaerke [auf] {level}",
  158. "(setze|stelle) [die] lautstaerke [auf] {level}",
  159. ],
  160. ),
  161. SystemIntentInfo(
  162. name="mute",
  163. description="Ton stumm schalten",
  164. examples=["Stumm", "Mute", "Ton aus"],
  165. patterns=["stumm", "mute", "ton aus"],
  166. ),
  167. SystemIntentInfo(
  168. name="unmute",
  169. description="Stummschaltung aufheben",
  170. examples=["Ton an", "Unmute", "Stummschaltung aus"],
  171. patterns=["ton an", "unmute", "stummschaltung aus"],
  172. ),
  173. ]
  174. # =============================================================================
  175. # Admin System-Intents (erfordern system_command + Login)
  176. # =============================================================================
  177. ADMIN_SYSTEM_INTENTS: list[SystemIntentInfo] = [
  178. SystemIntentInfo(
  179. name="system_login",
  180. description="Als Administrator anmelden (Passwort erforderlich)",
  181. security=IntentSecurityLevel.ADMIN,
  182. slots={"password": {"type": "str", "required": True, "description": "Admin-Passwort"}},
  183. examples=["Anmelden", "Login", "Authentifizieren"],
  184. patterns=["(anmelden|login|authentifizieren)"],
  185. ),
  186. SystemIntentInfo(
  187. name="system_logout",
  188. description="Administrator-Sitzung beenden",
  189. security=IntentSecurityLevel.ADMIN,
  190. examples=["Abmelden", "Logout", "Sitzung beenden"],
  191. patterns=["(abmelden|logout)", "sitzung beenden"],
  192. ),
  193. SystemIntentInfo(
  194. name="system_shutdown",
  195. description="System herunterfahren",
  196. security=IntentSecurityLevel.ADMIN,
  197. examples=["Herunterfahren", "Shutdown", "System ausschalten"],
  198. patterns=["(herunterfahren|shutdown)", "system ausschalten"],
  199. ),
  200. SystemIntentInfo(
  201. name="system_reboot",
  202. description="System neu starten",
  203. security=IntentSecurityLevel.ADMIN,
  204. examples=["Neustart", "Reboot", "System neu starten"],
  205. patterns=["(neustart|reboot)", "system neu starten"],
  206. ),
  207. SystemIntentInfo(
  208. name="system_update",
  209. description="System aktualisieren",
  210. security=IntentSecurityLevel.ADMIN,
  211. examples=["Update", "Aktualisieren", "System updaten"],
  212. patterns=["(update|aktualisieren)", "system (updaten|aktualisieren)"],
  213. ),
  214. SystemIntentInfo(
  215. name="plugin_reload",
  216. description="Plugin neu laden",
  217. security=IntentSecurityLevel.ADMIN,
  218. slots={"plugin_name": {"type": "str", "required": True}},
  219. examples=["Plugin neuladen", "Reload Plugin"],
  220. patterns=[
  221. "plugin {plugin_name} (neuladen|reload)",
  222. "(reload|neuladen) [plugin] {plugin_name}",
  223. ],
  224. ),
  225. SystemIntentInfo(
  226. name="plugin_enable",
  227. description="Plugin aktivieren",
  228. security=IntentSecurityLevel.ADMIN,
  229. slots={"plugin_name": {"type": "str", "required": True}},
  230. examples=["Plugin aktivieren", "Enable Plugin"],
  231. patterns=[
  232. "plugin {plugin_name} (aktivieren|enable)",
  233. "(aktiviere|enable) [plugin] {plugin_name}",
  234. ],
  235. ),
  236. SystemIntentInfo(
  237. name="plugin_disable",
  238. description="Plugin deaktivieren",
  239. security=IntentSecurityLevel.ADMIN,
  240. slots={"plugin_name": {"type": "str", "required": True}},
  241. examples=["Plugin deaktivieren", "Disable Plugin"],
  242. patterns=[
  243. "plugin {plugin_name} (deaktivieren|disable)",
  244. "(deaktiviere|disable) [plugin] {plugin_name}",
  245. ],
  246. ),
  247. SystemIntentInfo(
  248. name="satellite_list",
  249. description="Verbundene Satellites auflisten",
  250. security=IntentSecurityLevel.ADMIN,
  251. examples=["Zeige Satellites", "Welche Geräte sind verbunden?"],
  252. patterns=[
  253. "zeige satellites",
  254. "welche geraete sind verbunden",
  255. ],
  256. ),
  257. SystemIntentInfo(
  258. name="system_status",
  259. description="Detaillierten Systemstatus anzeigen",
  260. security=IntentSecurityLevel.ADMIN,
  261. examples=["Systemstatus", "Detaillierter Status", "System Info"],
  262. patterns=["systemstatus", "system (info|status)"],
  263. ),
  264. SystemIntentInfo(
  265. name="debug_mode",
  266. description="Debug-Modus ein/ausschalten",
  267. security=IntentSecurityLevel.ADMIN,
  268. slots={"enabled": {"type": "bool", "required": True}},
  269. examples=["Debug an", "Debug aus", "Debug-Modus aktivieren"],
  270. patterns=["debug (an|aus|ein)", "debug modus (aktivieren|deaktivieren)"],
  271. ),
  272. ]
  273. # Alle System-Intents kombiniert
  274. ALL_SYSTEM_INTENTS: list[SystemIntentInfo] = PUBLIC_SYSTEM_INTENTS + ADMIN_SYSTEM_INTENTS
  275. def _load_system_yaml(language: str = "de") -> dict[str, dict[str, Any]]:
  276. """Laedt YAML-Overrides fuer System-Intents."""
  277. try:
  278. from pathlib import Path
  279. from trixy_core.nlp.intent_loader import load_intent_yaml
  280. system_dir = Path(__file__).parent
  281. return load_intent_yaml(system_dir, language)
  282. except Exception:
  283. return {}
  284. def _apply_yaml_overrides(
  285. intent_dict: dict[str, Any],
  286. yaml_data: dict[str, dict[str, Any]],
  287. ) -> dict[str, Any]:
  288. """Wendet YAML-Overrides auf ein Intent-Dict an."""
  289. name = intent_dict.get("name", "")
  290. if name in yaml_data:
  291. yaml_entry = yaml_data[name]
  292. if "patterns" in yaml_entry:
  293. intent_dict["patterns"] = yaml_entry["patterns"]
  294. if "examples" in yaml_entry:
  295. intent_dict["examples"] = yaml_entry["examples"]
  296. return intent_dict
  297. def get_system_intents_for_context(
  298. wakeword_type: str = "custom",
  299. is_authenticated: bool = False,
  300. language: str = "de",
  301. admin_wakewords: list[str] | None = None,
  302. ) -> list[dict[str, Any]]:
  303. """
  304. Gibt die verfügbaren System-Intents für den aktuellen Kontext zurück.
  305. Laedt zusaetzlich Patterns/Examples aus intents_{language}.yml
  306. (falls vorhanden) und merged diese mit den hartkodierten Werten.
  307. Args:
  308. wakeword_type: Typ/Model-Name des Wakewords
  309. is_authenticated: Ist der Benutzer als Admin angemeldet?
  310. language: Sprachcode fuer YAML-Overrides
  311. admin_wakewords: Liste der Admin-Wakeword-Model-Names (aus Config)
  312. Returns:
  313. Liste der verfügbaren System-Intents als Dictionaries
  314. """
  315. yaml_data = _load_system_yaml(language)
  316. intents = []
  317. # Öffentliche Intents sind immer verfügbar
  318. for intent in PUBLIC_SYSTEM_INTENTS:
  319. d = intent.to_dict()
  320. if yaml_data:
  321. _apply_yaml_overrides(d, yaml_data)
  322. intents.append(d)
  323. # Admin-Intents nur bei Admin-Wakeword
  324. _admin_ww = set(admin_wakewords) if admin_wakewords else {"system_command"}
  325. if wakeword_type in _admin_ww:
  326. for intent in ADMIN_SYSTEM_INTENTS:
  327. # system_login ist immer verfügbar (um sich anzumelden)
  328. if intent.name == "system_login":
  329. d = intent.to_dict()
  330. if yaml_data:
  331. _apply_yaml_overrides(d, yaml_data)
  332. intents.append(d)
  333. # Andere Admin-Intents nur wenn authentifiziert
  334. elif is_authenticated:
  335. d = intent.to_dict()
  336. if yaml_data:
  337. _apply_yaml_overrides(d, yaml_data)
  338. intents.append(d)
  339. return intents
  340. def is_admin_intent(intent_name: str) -> bool:
  341. """Prüft ob ein Intent Admin-Rechte erfordert."""
  342. for intent in ADMIN_SYSTEM_INTENTS:
  343. if intent.name == intent_name:
  344. return True
  345. return False
  346. def is_system_intent(intent_name: str) -> bool:
  347. """Prüft ob ein Intent ein System-Intent ist."""
  348. for intent in ALL_SYSTEM_INTENTS:
  349. if intent.name == intent_name:
  350. return True
  351. return False
  352. def get_system_intent_info(intent_name: str) -> SystemIntentInfo | None:
  353. """Gibt die Info zu einem System-Intent zurück."""
  354. for intent in ALL_SYSTEM_INTENTS:
  355. if intent.name == intent_name:
  356. return intent
  357. return None