context.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. # -*- coding: utf-8 -*-
  2. """
  3. Conversation Context - Kontext-Verwaltung für Sessions.
  4. """
  5. from dataclasses import dataclass, field
  6. from datetime import datetime
  7. from enum import Enum
  8. from typing import Any
  9. class ContextScope(Enum):
  10. """Gültigkeitsbereich von Kontext-Variablen."""
  11. TURN = "turn" # Nur für aktuellen Turn
  12. SESSION = "session" # Für gesamte Session
  13. USER = "user" # Persistent für User
  14. GLOBAL = "global" # Global über alle Sessions
  15. @dataclass
  16. class ContextVariable:
  17. """Eine Kontext-Variable."""
  18. name: str
  19. value: Any
  20. scope: ContextScope = ContextScope.SESSION
  21. created_at: datetime = field(default_factory=datetime.now)
  22. updated_at: datetime = field(default_factory=datetime.now)
  23. metadata: dict[str, Any] = field(default_factory=dict)
  24. def update(self, value: Any) -> None:
  25. """Aktualisiert den Wert."""
  26. self.value = value
  27. self.updated_at = datetime.now()
  28. def to_dict(self) -> dict:
  29. """Konvertiert zu Dictionary."""
  30. return {
  31. "name": self.name,
  32. "value": self.value,
  33. "scope": self.scope.value,
  34. "created_at": self.created_at.isoformat(),
  35. "updated_at": self.updated_at.isoformat(),
  36. }
  37. class ConversationContext:
  38. """
  39. Kontext-Container für Conversation-Sessions.
  40. Verwaltet Variablen verschiedener Gültigkeitsbereiche:
  41. - TURN: Nur für aktuellen Gesprächsbeitrag
  42. - SESSION: Für gesamte Conversation-Session
  43. - USER: Persistent für einen User (über Sessions hinweg)
  44. - GLOBAL: Global verfügbar
  45. """
  46. def __init__(self):
  47. """Initialisiert den Kontext."""
  48. self._variables: dict[str, ContextVariable] = {}
  49. self._turn_variables: dict[str, ContextVariable] = {}
  50. def set(
  51. self,
  52. name: str,
  53. value: Any,
  54. scope: ContextScope = ContextScope.SESSION,
  55. metadata: dict | None = None,
  56. ) -> None:
  57. """
  58. Setzt eine Kontext-Variable.
  59. Args:
  60. name: Variablen-Name
  61. value: Wert
  62. scope: Gültigkeitsbereich
  63. metadata: Zusätzliche Metadaten
  64. """
  65. var = ContextVariable(
  66. name=name,
  67. value=value,
  68. scope=scope,
  69. metadata=metadata or {},
  70. )
  71. if scope == ContextScope.TURN:
  72. self._turn_variables[name] = var
  73. else:
  74. self._variables[name] = var
  75. def get(self, name: str, default: Any = None) -> Any:
  76. """
  77. Gibt Kontext-Variable zurück.
  78. Args:
  79. name: Variablen-Name
  80. default: Standard-Wert wenn nicht gefunden
  81. Returns:
  82. Wert oder Default
  83. """
  84. # Erst Turn-Variables prüfen
  85. if name in self._turn_variables:
  86. return self._turn_variables[name].value
  87. if name in self._variables:
  88. return self._variables[name].value
  89. return default
  90. def get_variable(self, name: str) -> ContextVariable | None:
  91. """
  92. Gibt Kontext-Variable mit Metadaten zurück.
  93. Args:
  94. name: Variablen-Name
  95. Returns:
  96. Variable oder None
  97. """
  98. if name in self._turn_variables:
  99. return self._turn_variables[name]
  100. return self._variables.get(name)
  101. def has(self, name: str) -> bool:
  102. """
  103. Prüft ob Variable existiert.
  104. Args:
  105. name: Variablen-Name
  106. Returns:
  107. True wenn vorhanden
  108. """
  109. return name in self._turn_variables or name in self._variables
  110. def remove(self, name: str) -> bool:
  111. """
  112. Entfernt Variable.
  113. Args:
  114. name: Variablen-Name
  115. Returns:
  116. True wenn entfernt
  117. """
  118. if name in self._turn_variables:
  119. del self._turn_variables[name]
  120. return True
  121. if name in self._variables:
  122. del self._variables[name]
  123. return True
  124. return False
  125. def clear_turn(self) -> None:
  126. """Löscht alle Turn-Variablen."""
  127. self._turn_variables.clear()
  128. def clear_scope(self, scope: ContextScope) -> None:
  129. """
  130. Löscht alle Variablen eines Scopes.
  131. Args:
  132. scope: Zu löschender Scope
  133. """
  134. if scope == ContextScope.TURN:
  135. self._turn_variables.clear()
  136. else:
  137. to_remove = [
  138. name for name, var in self._variables.items()
  139. if var.scope == scope
  140. ]
  141. for name in to_remove:
  142. del self._variables[name]
  143. def clear(self) -> None:
  144. """Löscht alle Variablen."""
  145. self._variables.clear()
  146. self._turn_variables.clear()
  147. def get_by_scope(self, scope: ContextScope) -> dict[str, Any]:
  148. """
  149. Gibt alle Variablen eines Scopes zurück.
  150. Args:
  151. scope: Scope
  152. Returns:
  153. Dictionary mit Variablen
  154. """
  155. if scope == ContextScope.TURN:
  156. return {name: var.value for name, var in self._turn_variables.items()}
  157. return {
  158. name: var.value
  159. for name, var in self._variables.items()
  160. if var.scope == scope
  161. }
  162. def get_all(self) -> dict[str, Any]:
  163. """
  164. Gibt alle Variablen zurück.
  165. Returns:
  166. Dictionary mit allen Variablen
  167. """
  168. result = {}
  169. # Session/User/Global zuerst
  170. for name, var in self._variables.items():
  171. result[name] = var.value
  172. # Turn überschreibt
  173. for name, var in self._turn_variables.items():
  174. result[name] = var.value
  175. return result
  176. def merge(self, other: "ConversationContext") -> None:
  177. """
  178. Merged anderen Kontext in diesen.
  179. Args:
  180. other: Anderer Kontext
  181. """
  182. for name, var in other._variables.items():
  183. self._variables[name] = var
  184. for name, var in other._turn_variables.items():
  185. self._turn_variables[name] = var
  186. def to_dict(self) -> dict:
  187. """Konvertiert zu Dictionary."""
  188. return {
  189. "variables": {
  190. name: var.to_dict()
  191. for name, var in self._variables.items()
  192. },
  193. "turn_variables": {
  194. name: var.to_dict()
  195. for name, var in self._turn_variables.items()
  196. },
  197. }
  198. # === Spezielle Kontext-Methoden für Conversations ===
  199. def set_intent(self, intent: str, confidence: float = 1.0) -> None:
  200. """Setzt erkannten Intent."""
  201. self.set("_intent", intent, ContextScope.TURN)
  202. self.set("_intent_confidence", confidence, ContextScope.TURN)
  203. def get_intent(self) -> str | None:
  204. """Gibt aktuellen Intent zurück."""
  205. return self.get("_intent")
  206. def set_entities(self, entities: dict[str, Any]) -> None:
  207. """Setzt extrahierte Entities."""
  208. self.set("_entities", entities, ContextScope.TURN)
  209. def get_entities(self) -> dict[str, Any]:
  210. """Gibt Entities zurück."""
  211. return self.get("_entities", {})
  212. def set_slot(self, name: str, value: Any) -> None:
  213. """Setzt Slot-Wert (für Dialoge)."""
  214. self.set(f"slot_{name}", value, ContextScope.SESSION)
  215. def get_slot(self, name: str, default: Any = None) -> Any:
  216. """Gibt Slot-Wert zurück."""
  217. return self.get(f"slot_{name}", default)
  218. def clear_slots(self) -> None:
  219. """Löscht alle Slots."""
  220. to_remove = [
  221. name for name in self._variables
  222. if name.startswith("slot_")
  223. ]
  224. for name in to_remove:
  225. del self._variables[name]
  226. @property
  227. def slots(self) -> dict[str, Any]:
  228. """Gibt alle Slots zurück."""
  229. return {
  230. name[5:]: var.value # Entferne "slot_" Prefix
  231. for name, var in self._variables.items()
  232. if name.startswith("slot_")
  233. }