fix: memory persistence + language auto-detection for translation workflow
- Upgrade memory/translation debug logs from debug to warning level - Auto-detect language preference from extracted memory facts - Persist language prefs to separate JSON file for reliability - Add translation detection logging - Use single linebreaks in translation menu Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
45
bot.py
45
bot.py
@@ -61,6 +61,7 @@ WILDFILES_BASE_URL = os.environ.get("WILDFILES_BASE_URL", "")
|
|||||||
WILDFILES_ORG = os.environ.get("WILDFILES_ORG", "")
|
WILDFILES_ORG = os.environ.get("WILDFILES_ORG", "")
|
||||||
USER_KEYS_FILE = os.environ.get("USER_KEYS_FILE", "/data/user_keys.json")
|
USER_KEYS_FILE = os.environ.get("USER_KEYS_FILE", "/data/user_keys.json")
|
||||||
MEMORIES_DIR = os.environ.get("MEMORIES_DIR", "/data/memories")
|
MEMORIES_DIR = os.environ.get("MEMORIES_DIR", "/data/memories")
|
||||||
|
LANGUAGE_PREFS_FILE = os.environ.get("LANGUAGE_PREFS_FILE", "/data/language_prefs.json")
|
||||||
MAX_MEMORIES_PER_USER = 50
|
MAX_MEMORIES_PER_USER = 50
|
||||||
|
|
||||||
SYSTEM_PROMPT = """You are a helpful AI assistant in a Matrix chat room.
|
SYSTEM_PROMPT = """You are a helpful AI assistant in a Matrix chat room.
|
||||||
@@ -220,6 +221,7 @@ class Bot:
|
|||||||
self._pending_connects: dict[str, str] = {} # matrix_user_id -> device_code
|
self._pending_connects: dict[str, str] = {} # matrix_user_id -> device_code
|
||||||
self._pending_translate: dict[str, dict] = {} # sender -> {text, detected_lang, room_id}
|
self._pending_translate: dict[str, dict] = {} # sender -> {text, detected_lang, room_id}
|
||||||
self._pending_reply: dict[str, dict] = {} # sender -> {target_lang}
|
self._pending_reply: dict[str, dict] = {} # sender -> {target_lang}
|
||||||
|
self.language_prefs: dict[str, str] = self._load_language_prefs() # user_id -> language
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _load_user_keys() -> dict[str, str]:
|
def _load_user_keys() -> dict[str, str]:
|
||||||
@@ -239,6 +241,23 @@ class Bot:
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to save user keys")
|
logger.exception("Failed to save user keys")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _load_language_prefs() -> dict[str, str]:
|
||||||
|
if os.path.exists(LANGUAGE_PREFS_FILE):
|
||||||
|
try:
|
||||||
|
with open(LANGUAGE_PREFS_FILE) as f:
|
||||||
|
return json.load(f)
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Failed to load language prefs, starting fresh")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _save_language_prefs(self):
|
||||||
|
try:
|
||||||
|
with open(LANGUAGE_PREFS_FILE, "w") as f:
|
||||||
|
json.dump(self.language_prefs, f)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Failed to save language prefs")
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
# Restore existing session or create new one
|
# Restore existing session or create new one
|
||||||
if os.path.exists(CREDS_FILE):
|
if os.path.exists(CREDS_FILE):
|
||||||
@@ -488,10 +507,21 @@ class Bot:
|
|||||||
for fact in new_facts:
|
for fact in new_facts:
|
||||||
if isinstance(fact, str) and fact.strip():
|
if isinstance(fact, str) and fact.strip():
|
||||||
existing.append({"fact": fact.strip(), "created": now, "source_room": room_id})
|
existing.append({"fact": fact.strip(), "created": now, "source_room": room_id})
|
||||||
|
# Auto-detect language preference from new facts
|
||||||
|
fl = fact.lower()
|
||||||
|
if "language" in fl or "speaks" in fl or "prefers" in fl:
|
||||||
|
for lang in ["English", "German", "French", "Spanish", "Italian",
|
||||||
|
"Portuguese", "Dutch", "Russian", "Chinese", "Japanese",
|
||||||
|
"Korean", "Arabic", "Turkish", "Greek", "Hebrew"]:
|
||||||
|
if lang.lower() in fl:
|
||||||
|
self.language_prefs[sender] = lang
|
||||||
|
self._save_language_prefs()
|
||||||
|
logger.info("Auto-detected language preference: %s for %s", lang, sender)
|
||||||
|
break
|
||||||
|
|
||||||
return existing
|
return existing
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Memory extraction failed", exc_info=True)
|
logger.warning("Memory extraction failed", exc_info=True)
|
||||||
return existing
|
return existing
|
||||||
|
|
||||||
async def _detect_language(self, text: str) -> str:
|
async def _detect_language(self, text: str) -> str:
|
||||||
@@ -531,7 +561,11 @@ class Bot:
|
|||||||
return f"[Translation failed] {text}"
|
return f"[Translation failed] {text}"
|
||||||
|
|
||||||
def _get_preferred_language(self, user_id: str) -> str:
|
def _get_preferred_language(self, user_id: str) -> str:
|
||||||
"""Extract user's preferred language from memories, default English."""
|
"""Get user's preferred language from explicit prefs, then memories, default English."""
|
||||||
|
# Check explicit language preference first
|
||||||
|
if user_id in self.language_prefs:
|
||||||
|
return self.language_prefs[user_id]
|
||||||
|
# Fallback: scan memories
|
||||||
memories = self._load_memories(user_id)
|
memories = self._load_memories(user_id)
|
||||||
known_langs = [
|
known_langs = [
|
||||||
"English", "German", "French", "Spanish", "Italian", "Portuguese",
|
"English", "German", "French", "Spanish", "Italian", "Portuguese",
|
||||||
@@ -639,6 +673,7 @@ class Bot:
|
|||||||
if is_dm and not body.startswith("!ai") and not image_data:
|
if is_dm and not body.startswith("!ai") and not image_data:
|
||||||
preferred_lang = self._get_preferred_language(sender)
|
preferred_lang = self._get_preferred_language(sender)
|
||||||
detected_lang = await self._detect_language(body)
|
detected_lang = await self._detect_language(body)
|
||||||
|
logger.info("Translation check: detected=%s, preferred=%s, len=%d", detected_lang, preferred_lang, len(body))
|
||||||
if (
|
if (
|
||||||
detected_lang != "Unknown"
|
detected_lang != "Unknown"
|
||||||
and detected_lang.lower() != preferred_lang.lower()
|
and detected_lang.lower() != preferred_lang.lower()
|
||||||
@@ -650,7 +685,7 @@ class Bot:
|
|||||||
"room_id": room.room_id,
|
"room_id": room.room_id,
|
||||||
}
|
}
|
||||||
menu = (
|
menu = (
|
||||||
f"This looks like **{detected_lang}**. What would you like?\n\n"
|
f"This looks like **{detected_lang}**. What would you like?\n"
|
||||||
f"1️⃣ **Translate to {preferred_lang}**\n"
|
f"1️⃣ **Translate to {preferred_lang}**\n"
|
||||||
f"2️⃣ **Help me reply in {detected_lang}** (type your response, I'll translate)\n"
|
f"2️⃣ **Help me reply in {detected_lang}** (type your response, I'll translate)\n"
|
||||||
f"3️⃣ **Just respond normally**"
|
f"3️⃣ **Just respond normally**"
|
||||||
@@ -1181,8 +1216,10 @@ class Bot:
|
|||||||
self._save_memories(sender, updated)
|
self._save_memories(sender, updated)
|
||||||
logger.info("Saved %d new memories for %s (total: %d)",
|
logger.info("Saved %d new memories for %s (total: %d)",
|
||||||
len(updated) - len(memories), sender, len(updated))
|
len(updated) - len(memories), sender, len(updated))
|
||||||
|
else:
|
||||||
|
logger.info("No new memories extracted for %s", sender)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Memory save failed", exc_info=True)
|
logger.warning("Memory save failed", exc_info=True)
|
||||||
|
|
||||||
# Auto-rename: only for group rooms with explicit opt-in (not DMs)
|
# Auto-rename: only for group rooms with explicit opt-in (not DMs)
|
||||||
if room.room_id in self.auto_rename_rooms:
|
if room.room_id in self.auto_rename_rooms:
|
||||||
|
|||||||
Reference in New Issue
Block a user