diff --git a/bot/api.py b/bot/api.py index ef65f00..a47a232 100644 --- a/bot/api.py +++ b/bot/api.py @@ -307,6 +307,13 @@ def set_language_mode(language: str = "english"): model_used = globals.JAPANESE_TEXT_MODEL if language.lower() == "japanese" else globals.TEXT_MODEL logger.info(f"Language mode set to {language.lower()} (using {model_used})") + # Persist so it survives restarts + try: + from config_manager import config_manager + config_manager.set("discord.language_mode", language.lower(), persist=True) + except Exception: + pass + return { "status": "ok", "language_mode": language.lower(), @@ -2963,6 +2970,14 @@ def set_voice_debug_mode(enabled: bool = Form(...)): """Set voice debug mode (shows transcriptions and responses in text channel)""" globals.VOICE_DEBUG_MODE = enabled logger.info(f"Voice debug mode set to: {enabled}") + + # Persist so it survives restarts + try: + from config_manager import config_manager + config_manager.set("voice.debug_mode", enabled, persist=True) + except Exception: + pass + return { "status": "ok", "debug_mode": enabled, @@ -3004,6 +3019,14 @@ async def toggle_cat_integration(enabled: bool = Form(...)): """Toggle Cheshire Cat integration on/off.""" globals.USE_CHESHIRE_CAT = enabled logger.info(f"🐱 Cheshire Cat integration {'ENABLED' if enabled else 'DISABLED'}") + + # Persist so it survives restarts + try: + from config_manager import config_manager + config_manager.set("memory.use_cheshire_cat", enabled, persist=True) + except Exception: + pass + return { "success": True, "enabled": globals.USE_CHESHIRE_CAT, diff --git a/bot/bot.py b/bot/bot.py index f032c38..27ce63e 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -105,6 +105,10 @@ async def on_ready(): from utils.bipolar_mode import restore_bipolar_mode_on_startup restore_bipolar_mode_on_startup() + # Restore runtime settings (language, debug flags, etc.) from config_runtime.yaml + from config_manager import config_manager + config_manager.restore_runtime_settings() + # Initialize DM interaction analyzer if globals.OWNER_USER_ID and globals.OWNER_USER_ID != 0: init_dm_analyzer(globals.OWNER_USER_ID) diff --git a/bot/config_manager.py b/bot/config_manager.py index cb2daa2..3c5fdc0 100644 --- a/bot/config_manager.py +++ b/bot/config_manager.py @@ -102,6 +102,54 @@ class ConfigManager: except Exception as e: logger.error(f"❌ Failed to load GPU state: {e}") + def restore_runtime_settings(self): + """ + Restore persisted runtime settings from config_runtime.yaml into globals. + + Called once at startup (in on_ready) so that settings changed via the + Web UI or API survive bot restarts. + + Settings with their own persistence (EVIL_MODE, BIPOLAR_MODE) are + handled by their respective modules and are intentionally skipped here. + """ + import globals as g + + # Map: config_runtime.yaml key path -> (globals attribute, converter) + _SETTINGS_MAP = { + "discord.language_mode": ("LANGUAGE_MODE", str), + "autonomous.debug_mode": ("AUTONOMOUS_DEBUG", bool), + "voice.debug_mode": ("VOICE_DEBUG_MODE", bool), + "memory.use_cheshire_cat": ("USE_CHESHIRE_CAT", bool), + "gpu.prefer_amd": ("PREFER_AMD_GPU", bool), + } + + restored = [] + + for key_path, (attr, converter) in _SETTINGS_MAP.items(): + value = self._get_nested_value(self.runtime_config, key_path) + if value is not None: + try: + setattr(g, attr, converter(value)) + restored.append(f"{attr}={getattr(g, attr)}") + except (ValueError, TypeError) as exc: + logger.warning(f"âš ī¸ Could not restore {key_path}: {exc}") + + # DM mood needs special handling (load description too) + dm_mood = self._get_nested_value(self.runtime_config, "runtime.mood.dm_mood") + if dm_mood and isinstance(dm_mood, str) and dm_mood in getattr(g, "AVAILABLE_MOODS", []): + g.DM_MOOD = dm_mood + try: + from utils.moods import load_mood_description + g.DM_MOOD_DESCRIPTION = load_mood_description(dm_mood) + except Exception: + g.DM_MOOD_DESCRIPTION = f"I'm feeling {dm_mood} today." + restored.append(f"DM_MOOD={dm_mood}") + + if restored: + logger.info(f"🔄 Restored {len(restored)} runtime settings: {', '.join(restored)}") + else: + logger.debug("â„šī¸ No runtime settings to restore") + def get(self, key_path: str, default: Any = None) -> Any: """ Get configuration value with priority system.