Files
miku-discord/bot/routes/config.py

185 lines
6.0 KiB
Python
Raw Normal View History

"""Configuration management routes: get/set/reset/validate config."""
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
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 JSONResponse(status_code=500, content={"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 JSONResponse(status_code=500, content={"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 JSONResponse(status_code=500, content={"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 JSONResponse(status_code=400, content={"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 JSONResponse(status_code=500, content={"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 JSONResponse(status_code=500, content={"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 JSONResponse(status_code=500, content={"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 JSONResponse(status_code=500, content={"success": False, "error": str(e)})