Hausmeister: Session-Recovery nach Restart + robustes Topic-Matching mit Aliassen
This commit is contained in:
parent
74ac5a2ce5
commit
5e1b0efefd
1 changed files with 68 additions and 7 deletions
|
|
@ -22,6 +22,43 @@ _token = None
|
|||
SESSION_TIMEOUT = 1800 # 30 Minuten Inaktivitaet = neue Session
|
||||
_active_sessions: dict[str, dict] = {} # channel_key -> {id, last_activity}
|
||||
|
||||
TOPIC_ALIASES = {
|
||||
"container": ["containers", "lxc", "lxc-container", "lxc container", "ct"],
|
||||
"server": ["servers", "proxmox", "pve", "host", "hosts"],
|
||||
"backup": ["backups", "pbs", "snapshot", "snapshots"],
|
||||
"projekt": ["projekte", "project", "projects"],
|
||||
"mail": ["mails", "email", "e-mail"],
|
||||
}
|
||||
|
||||
|
||||
def _normalize(text: str) -> str:
|
||||
"""Normalisiert Text fuer robustes Matching."""
|
||||
t = text.lower().strip()
|
||||
for old, new in [("ä", "ae"), ("ö", "oe"), ("ü", "ue"), ("ß", "ss")]:
|
||||
t = t.replace(old, new)
|
||||
t = t.replace("-", " ").replace("_", " ")
|
||||
return t
|
||||
|
||||
|
||||
def _topic_matches(text: str, topic: str) -> bool:
|
||||
"""Prueft ob ein Topic im normalisierten Text vorkommt (inkl. Aliase)."""
|
||||
norm_text = _normalize(text)
|
||||
norm_topic = _normalize(topic)
|
||||
if norm_topic in norm_text:
|
||||
return True
|
||||
aliases = TOPIC_ALIASES.get(norm_topic, [])
|
||||
for alias in aliases:
|
||||
if _normalize(alias) in norm_text:
|
||||
return True
|
||||
for base, alias_list in TOPIC_ALIASES.items():
|
||||
if norm_topic in [_normalize(a) for a in alias_list] or norm_topic == base:
|
||||
if base in norm_text:
|
||||
return True
|
||||
for a in alias_list:
|
||||
if _normalize(a) in norm_text:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _ensure_config():
|
||||
global _cfg, _base_url, _token
|
||||
|
|
@ -66,14 +103,39 @@ def _get(path: str, params: dict = None) -> Optional[dict]:
|
|||
return None
|
||||
|
||||
|
||||
def _patch(path: str, data: dict) -> Optional[dict]:
|
||||
_ensure_config()
|
||||
if not _base_url:
|
||||
return None
|
||||
try:
|
||||
r = requests.patch(f"{_base_url}{path}", json=data, headers=_headers(), timeout=5)
|
||||
if r.ok:
|
||||
return r.json()
|
||||
log.warning("Memory API PATCH %s: %s %s", path, r.status_code, r.text[:200])
|
||||
except Exception as e:
|
||||
log.warning("Memory API PATCH %s: %s", path, e)
|
||||
return None
|
||||
|
||||
|
||||
def get_or_create_session(channel_key: str, source: str = "telegram") -> Optional[str]:
|
||||
"""Gibt eine aktive Session-ID zurueck oder erstellt eine neue."""
|
||||
"""Gibt eine aktive Session-ID zurueck oder erstellt eine neue.
|
||||
Ueberlebt Bot-Restarts durch API-Lookup der letzten Session."""
|
||||
now = time.time()
|
||||
cached = _active_sessions.get(channel_key)
|
||||
if cached and (now - cached["last_activity"]) < SESSION_TIMEOUT:
|
||||
cached["last_activity"] = now
|
||||
return cached["id"]
|
||||
|
||||
# Nach Restart: letzte Session vom API holen
|
||||
latest = _get("/sessions/latest", {"channel_key": channel_key})
|
||||
if latest and latest.get("id"):
|
||||
last_at = latest.get("last_activity_at", 0)
|
||||
if (now - last_at) < SESSION_TIMEOUT:
|
||||
_active_sessions[channel_key] = {"id": latest["id"], "last_activity": now}
|
||||
_patch(f"/sessions/{latest['id']}", {})
|
||||
log.info("Session wiederhergestellt nach Restart: %s", latest["id"][:12])
|
||||
return latest["id"]
|
||||
|
||||
result = _post("/sessions", {"source": source, "channel_key": channel_key})
|
||||
if result and "id" in result:
|
||||
_active_sessions[channel_key] = {"id": result["id"], "last_activity": now}
|
||||
|
|
@ -150,29 +212,28 @@ def get_session_summary(session_id: str, limit: int = 20, topic: str = None) ->
|
|||
return "Keine Themen in dieser Session."
|
||||
|
||||
if topic:
|
||||
topic_lower = topic.lower()
|
||||
matching = []
|
||||
other_topics = []
|
||||
for q, a in exchanges:
|
||||
combined = (q + " " + (a or "")).lower()
|
||||
if topic_lower in combined:
|
||||
combined = q + " " + (a or "")
|
||||
if _topic_matches(combined, topic):
|
||||
matching.append((q, a))
|
||||
else:
|
||||
other_topics.append(q[:80])
|
||||
|
||||
lines = []
|
||||
if matching:
|
||||
lines.append("Zum Thema '" + topic + "' (" + str(len(matching)) + " Punkte):")
|
||||
lines.append("Zum Thema '" + topic + "' (" + str(len(matching)) + " Treffer):")
|
||||
for i, (q, a) in enumerate(matching, 1):
|
||||
line = str(i) + ". Frage: " + q
|
||||
if a:
|
||||
line += "\n Antwort: " + a
|
||||
lines.append(line)
|
||||
else:
|
||||
lines.append("Zum Thema '" + topic + "' wurde nichts direkt besprochen.")
|
||||
lines.append("Zum Thema '" + topic + "' wurde in dieser Session nichts direkt besprochen.")
|
||||
|
||||
if other_topics:
|
||||
lines.append("\nSonstige Themen: " + ", ".join(other_topics))
|
||||
lines.append("\nSonstige Themen der Session: " + ", ".join(other_topics))
|
||||
return "\n".join(lines)
|
||||
|
||||
lines = ["Session (" + str(len(exchanges)) + " Themen):"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue