"""Configuration management routes: get/set/reset/validate config.""" from fastapi import APIRouter, Request import globals from utils.logger import get_logger logger = get_logger('api') router = APIRouter() @router.get("/config") async def get_full_config(): """ Get full configuration including static, runtime, and state. Useful for debugging and config display in UI. """ try: from config_manager import config_manager full_config = config_manager.get_full_config() return { "success": True, "config": full_config } except Exception as e: logger.error(f"Failed to get config: {e}") return {"success": False, "error": str(e)} @router.get("/config/static") async def get_static_config(): """ Get static configuration from config.yaml. These are default values that can be overridden at runtime. """ try: from config_manager import config_manager return { "success": True, "config": config_manager.static_config } except Exception as e: logger.error(f"Failed to get static config: {e}") return {"success": False, "error": str(e)} @router.get("/config/runtime") async def get_runtime_config(): """ Get runtime configuration overrides. These are values changed via Web UI that override config.yaml. """ try: from config_manager import config_manager return { "success": True, "config": config_manager.runtime_config, "path": str(config_manager.runtime_config_path) } except Exception as e: logger.error(f"Failed to get runtime config: {e}") return {"success": False, "error": str(e)} @router.post("/config/set") async def set_config_value(request: Request): """ Set a configuration value with optional persistence. Body: { "key_path": "discord.language_mode", // Dot-separated path "value": "japanese", "persist": true // Save to config_runtime.yaml } """ try: data = await request.json() key_path = data.get("key_path") value = data.get("value") persist = data.get("persist", True) if not key_path: return {"success": False, "error": "key_path is required"} from config_manager import config_manager config_manager.set(key_path, value, persist=persist) # ── Sync globals for every runtime-relevant key path ── _GLOBALS_SYNC = { "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), } if key_path in _GLOBALS_SYNC: attr, converter = _GLOBALS_SYNC[key_path] setattr(globals, attr, converter(value)) elif key_path == "runtime.mood.dm_mood": # DM mood needs description loaded alongside if isinstance(value, str) and value in getattr(globals, "AVAILABLE_MOODS", []): globals.DM_MOOD = value try: from utils.moods import load_mood_description globals.DM_MOOD_DESCRIPTION = load_mood_description(value) except Exception: globals.DM_MOOD_DESCRIPTION = f"I'm feeling {value} today." return { "success": True, "message": f"Set {key_path} = {value}", "persisted": persist } except Exception as e: logger.error(f"Failed to set config: {e}") return {"success": False, "error": str(e)} @router.post("/config/reset") async def reset_config(request: Request): """ Reset configuration to defaults. Body: { "key_path": "discord.language_mode", // Optional: reset specific key "persist": true // Remove from config_runtime.yaml } If key_path is omitted, resets all runtime config to defaults. """ try: data = await request.json() key_path = data.get("key_path") persist = data.get("persist", True) from config_manager import config_manager config_manager.reset_to_defaults(key_path) return { "success": True, "message": f"Reset {key_path or 'all config'} to defaults" } except Exception as e: logger.error(f"Failed to reset config: {e}") return {"success": False, "error": str(e)} @router.post("/config/validate") async def validate_config_endpoint(): """ Validate current configuration. Returns list of errors if validation fails. """ try: from config_manager import config_manager is_valid, errors = config_manager.validate_config() return { "success": is_valid, "is_valid": is_valid, "errors": errors } except Exception as e: logger.error(f"Failed to validate config: {e}") return {"success": False, "error": str(e)} @router.get("/config/state") async def get_config_state(): """ Get runtime state (not persisted config). These are transient values like current mood, evil mode, etc. """ try: from config_manager import config_manager return { "success": True, "state": config_manager.runtime_state } except Exception as e: logger.error(f"Failed to get config state: {e}") return {"success": False, "error": str(e)}