provider.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. # -*- coding: utf-8 -*-
  2. """
  3. NLP Provider Interfaces.
  4. Definiert die Basisklassen und Datenstrukturen für NLP-Provider.
  5. """
  6. from abc import ABC, abstractmethod
  7. from dataclasses import dataclass, field
  8. from enum import Enum, auto
  9. from typing import Any, TYPE_CHECKING
  10. if TYPE_CHECKING:
  11. from trixy_core.conversation.session import ConversationSession
  12. class NLPState(Enum):
  13. """Status eines NLP-Providers."""
  14. UNINITIALIZED = auto()
  15. INITIALIZING = auto()
  16. READY = auto()
  17. PROCESSING = auto()
  18. ERROR = auto()
  19. SHUTDOWN = auto()
  20. @dataclass
  21. class NLPConfig:
  22. """
  23. Konfiguration für einen NLP-Provider.
  24. Attributes:
  25. backend: Name des Backends (z.B. "llama_cpp", "ollama")
  26. model_name: Name des Modells
  27. model_path: Optionaler Pfad zum Modell
  28. use_gpu: GPU-Beschleunigung verwenden
  29. num_threads: Anzahl CPU-Threads
  30. temperature: Sampling-Temperatur (0.0 = deterministisch)
  31. max_tokens: Maximale Antwortlänge
  32. context_window: Größe des Kontextfensters
  33. extra: Zusätzliche backend-spezifische Optionen
  34. """
  35. backend: str = "llama_cpp"
  36. model_name: str = ""
  37. model_path: str | None = None
  38. use_gpu: bool = False
  39. num_threads: int = 4
  40. temperature: float = 0.1
  41. max_tokens: int = 256
  42. context_window: int = 2048
  43. extra: dict[str, Any] = field(default_factory=dict)
  44. @dataclass
  45. class NLPContext:
  46. """
  47. Kontext für eine NLP-Anfrage.
  48. Enthält alle relevanten Informationen für die Intent-Erkennung.
  49. Attributes:
  50. text: Der zu verarbeitende Text (STT-Ergebnis)
  51. satellite_id: ID des Satellites
  52. room_id: Raum-ID
  53. session_id: Konversations-Session-ID
  54. session: Optionale Konversations-Session für Verlauf
  55. available_intents: Liste verfügbarer Intents mit Metadaten
  56. user_context: Zusätzlicher Benutzerkontext
  57. language: Sprachcode (z.B. "de", "en")
  58. """
  59. text: str
  60. satellite_id: str = ""
  61. room_id: str = ""
  62. session_id: str = ""
  63. session: "ConversationSession | None" = None
  64. available_intents: list[dict[str, Any]] = field(default_factory=list)
  65. user_context: dict[str, Any] = field(default_factory=dict)
  66. language: str = "de"
  67. def get_conversation_history(self, max_turns: int = 5) -> list[dict[str, str]]:
  68. """
  69. Gibt die letzten N Konversations-Turns zurück.
  70. Args:
  71. max_turns: Maximale Anzahl Turns
  72. Returns:
  73. Liste von {"role": "user"|"assistant", "content": "..."}
  74. """
  75. if self.session is None:
  76. return []
  77. history = []
  78. turns = list(self.session.turns)[-max_turns:]
  79. for turn in turns:
  80. if turn.turn_type.name.startswith("USER"):
  81. history.append({"role": "user", "content": turn.content})
  82. elif turn.turn_type.name.startswith("ASSISTANT"):
  83. history.append({"role": "assistant", "content": turn.content})
  84. return history
  85. @dataclass
  86. class NLPResult:
  87. """
  88. Ergebnis einer NLP-Verarbeitung.
  89. Attributes:
  90. intent: Erkannter Intent-Name
  91. confidence: Konfidenz der Erkennung (0.0 - 1.0)
  92. slots: Extrahierte Slot-Werte
  93. response_text: Generierte Antwort (optional, für LLM-basierte NLP)
  94. raw_output: Rohausgabe des NLP-Modells
  95. processing_time: Verarbeitungszeit in Sekunden
  96. success: Ob die Verarbeitung erfolgreich war
  97. error: Fehlermeldung falls nicht erfolgreich
  98. """
  99. intent: str = ""
  100. confidence: float = 0.0
  101. slots: dict[str, Any] = field(default_factory=dict)
  102. response_text: str = ""
  103. raw_output: str = ""
  104. processing_time: float = 0.0
  105. success: bool = True
  106. error: str = ""
  107. @classmethod
  108. def failure(cls, error: str) -> "NLPResult":
  109. """Erstellt ein Fehler-Ergebnis."""
  110. return cls(success=False, error=error)
  111. def has_response(self) -> bool:
  112. """Prüft ob eine Antwort generiert wurde."""
  113. return bool(self.response_text)
  114. class NLPProvider(ABC):
  115. """
  116. Abstrakte Basisklasse für NLP-Provider.
  117. Ein NLP-Provider verarbeitet Text und erkennt Intents mit Slots.
  118. Optional kann er auch direkt Antworten generieren (LLM-basiert).
  119. Beispiel:
  120. class LLMNLPProvider(NLPProvider):
  121. async def initialize(self, config: NLPConfig) -> bool:
  122. self._model = load_model(config.model_path)
  123. return True
  124. async def process(self, context: NLPContext) -> NLPResult:
  125. response = await self._model.generate(context.text)
  126. return NLPResult(
  127. intent=response.intent,
  128. confidence=response.confidence,
  129. slots=response.slots,
  130. response_text=response.text
  131. )
  132. """
  133. def __init__(self) -> None:
  134. """Initialisiert den Provider."""
  135. self._state = NLPState.UNINITIALIZED
  136. self._config: NLPConfig | None = None
  137. @property
  138. def state(self) -> NLPState:
  139. """Aktueller Status des Providers."""
  140. return self._state
  141. @property
  142. def is_ready(self) -> bool:
  143. """Ist der Provider einsatzbereit?"""
  144. return self._state == NLPState.READY
  145. @property
  146. def config(self) -> NLPConfig | None:
  147. """Aktuelle Konfiguration."""
  148. return self._config
  149. @abstractmethod
  150. async def initialize(self, config: NLPConfig) -> bool:
  151. """
  152. Initialisiert den Provider mit der gegebenen Konfiguration.
  153. Args:
  154. config: Provider-Konfiguration
  155. Returns:
  156. True bei erfolgreicher Initialisierung
  157. """
  158. pass
  159. @abstractmethod
  160. async def process(self, context: NLPContext) -> NLPResult:
  161. """
  162. Verarbeitet Text und erkennt Intents.
  163. Args:
  164. context: NLP-Kontext mit Text und Metadaten
  165. Returns:
  166. NLP-Ergebnis mit Intent, Slots und optionaler Antwort
  167. """
  168. pass
  169. @abstractmethod
  170. async def shutdown(self) -> None:
  171. """Fährt den Provider herunter und gibt Ressourcen frei."""
  172. pass
  173. async def health_check(self) -> bool:
  174. """
  175. Führt einen Health-Check durch.
  176. Returns:
  177. True wenn der Provider funktionsfähig ist
  178. """
  179. return self._state == NLPState.READY
  180. def get_capabilities(self) -> list[str]:
  181. """
  182. Gibt die Fähigkeiten des Providers zurück.
  183. Returns:
  184. Liste von Fähigkeiten (z.B. ["intent", "response", "entity"])
  185. """
  186. return ["intent"]
  187. def supports_streaming(self) -> bool:
  188. """
  189. Unterstützt der Provider Streaming-Antworten?
  190. Returns:
  191. True wenn Streaming unterstützt wird
  192. """
  193. return False