Files
miku-discord/bot/config_manager.py

413 lines
15 KiB
Python
Raw Normal View History

Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
"""
Unified Configuration Manager for Miku Discord Bot.
Handles:
- Static configuration from config.yaml
- Runtime overrides from Web UI
- Per-server configuration
- Priority system: Runtime > Static > Defaults
- Persistence of runtime changes
"""
import json
from pathlib import Path
from typing import Any, Dict, Optional
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
from datetime import datetime
import yaml
from config import CONFIG, SECRETS
from utils.logger import get_logger
logger = get_logger('config_manager')
class ConfigManager:
"""
Unified configuration manager with runtime overrides.
Priority:
1. Runtime overrides (from Web UI, API, CLI)
2. Static config (from config.yaml)
3. Hardcoded defaults (fallback)
"""
def __init__(self, config_path: Optional[str] = None):
"""Initialize configuration manager."""
self.config_path = Path(config_path) if config_path else Path(__file__).parent.parent / "config.yaml"
# Memory directory for server configs and state
# This directory is volume-mounted in Docker (./bot/memory:/app/memory)
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
self.memory_dir = Path(__file__).parent / "memory"
self.memory_dir.mkdir(exist_ok=True)
# Runtime config must live inside memory_dir so it persists across container restarts
self.runtime_config_path = self.memory_dir / "config_runtime.yaml"
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
# Load configurations
self.static_config: Dict = self._load_static_config()
self.runtime_config: Dict = self._load_runtime_config()
# GPU state (the only piece of runtime_state that is persisted to its own file)
self._current_gpu: str = "nvidia"
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
# Load persisted state (GPU)
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
self._load_runtime_state()
logger.info("✅ ConfigManager initialized")
def _load_static_config(self) -> Dict:
"""Load static configuration from config.yaml."""
if not self.config_path.exists():
logger.warning(f"⚠️ config.yaml not found: {self.config_path}")
return {}
try:
with open(self.config_path, "r") as f:
config = yaml.safe_load(f) or {}
logger.debug(f"✅ Loaded static config from {self.config_path}")
return config
except Exception as e:
logger.error(f"❌ Failed to load config.yaml: {e}")
return {}
def _load_runtime_config(self) -> Dict:
"""Load runtime overrides from config_runtime.yaml."""
if not self.runtime_config_path.exists():
logger.debug(" config_runtime.yaml not found (no overrides)")
return {}
try:
with open(self.runtime_config_path, "r") as f:
config = yaml.safe_load(f) or {}
logger.debug(f"✅ Loaded runtime config from {self.runtime_config_path}")
return config
except Exception as e:
logger.error(f"❌ Failed to load config_runtime.yaml: {e}")
return {}
def _load_runtime_state(self):
"""Load runtime state from memory files."""
# Load GPU state
gpu_state_file = self.memory_dir / "gpu_state.json"
try:
if gpu_state_file.exists():
with open(gpu_state_file, "r") as f:
gpu_state = json.load(f)
self._current_gpu = gpu_state.get("current_gpu", "nvidia")
logger.debug(f"✅ Loaded GPU state: {self._current_gpu}")
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
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")
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
def get(self, key_path: str, default: Any = None) -> Any:
"""
Get configuration value with priority system.
Args:
key_path: Dot-separated path (e.g., "discord.language_mode")
default: Fallback value if not found
Returns:
Configuration value (runtime > static > default)
"""
# Try runtime config first
value = self._get_nested_value(self.runtime_config, key_path)
if value is not None:
logger.debug(f"⚡ Runtime config: {key_path} = {value}")
return value
# Try static config second
value = self._get_nested_value(self.static_config, key_path)
if value is not None:
logger.debug(f"📄 Static config: {key_path} = {value}")
return value
# Return default
logger.debug(f"⚙️ Default value: {key_path} = {default}")
return default
def _get_nested_value(self, config: Dict, key_path: str) -> Any:
"""Get nested value from config using dot notation."""
keys = key_path.split(".")
value = config
for key in keys:
if isinstance(value, dict) and key in value:
value = value[key]
else:
return None
return value
def set(self, key_path: str, value: Any, persist: bool = True):
"""
Set configuration value.
Args:
key_path: Dot-separated path (e.g., "discord.language_mode")
value: New value to set
persist: Whether to save to config_runtime.yaml
"""
# Set in runtime config
keys = key_path.split(".")
config = self.runtime_config
for key in keys[:-1]:
if key not in config:
config[key] = {}
config = config[key]
config[keys[-1]] = value
logger.info(f"✅ Config set: {key_path} = {value}")
# Persist if requested
if persist:
self.save_runtime_config()
def save_runtime_config(self):
"""Save runtime configuration to config_runtime.yaml."""
try:
with open(self.runtime_config_path, "w") as f:
yaml.dump(self.runtime_config, f, default_flow_style=False)
logger.info(f"💾 Saved runtime config to {self.runtime_config_path}")
except Exception as e:
logger.error(f"❌ Failed to save runtime config: {e}")
def reset_to_defaults(self, key_path: Optional[str] = None):
"""
Reset configuration to defaults.
Clears runtime overrides from config_runtime.yaml AND resets the
corresponding globals to their default values so the change takes
effect immediately without a restart.
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
Args:
key_path: Specific key to reset, or None to reset all runtime config
"""
import globals as g
from config import CONFIG
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
if key_path:
# Remove specific key from runtime config
self._remove_nested_key(self.runtime_config, key_path)
logger.info(f"🔄 Reset {key_path} to default")
else:
# Clear all runtime config
self.runtime_config = {}
logger.info("🔄 Reset all config to defaults")
self.save_runtime_config()
# ---- Reset live globals to match defaults ----
# Map: config_runtime key path -> (globals attr, default from CONFIG)
_DEFAULTS_MAP = {
"discord.language_mode": ("LANGUAGE_MODE", CONFIG.discord.language_mode),
"autonomous.debug_mode": ("AUTONOMOUS_DEBUG", CONFIG.autonomous.debug_mode),
"voice.debug_mode": ("VOICE_DEBUG_MODE", CONFIG.voice.debug_mode),
"memory.use_cheshire_cat": ("USE_CHESHIRE_CAT", CONFIG.cheshire_cat.enabled),
"gpu.prefer_amd": ("PREFER_AMD_GPU", CONFIG.gpu.prefer_amd),
}
reset_items = []
if key_path:
# Reset only the specific global
if key_path in _DEFAULTS_MAP:
attr, default = _DEFAULTS_MAP[key_path]
setattr(g, attr, default)
reset_items.append(f"{attr}={default}")
else:
# Reset all globals to defaults
for kp, (attr, default) in _DEFAULTS_MAP.items():
setattr(g, attr, default)
reset_items.append(f"{attr}={default}")
# Also reset DM mood to neutral
g.DM_MOOD = "neutral"
g.DM_MOOD_DESCRIPTION = "I'm feeling neutral and balanced today."
reset_items.append("DM_MOOD=neutral")
if reset_items:
logger.info(f"🔄 Reset {len(reset_items)} globals: {', '.join(reset_items)}")
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
def _remove_nested_key(self, config: Dict, key_path: str):
"""Remove nested key from config."""
keys = key_path.split(".")
obj = config
for key in keys[:-1]:
if isinstance(obj, dict) and key in obj:
obj = obj[key]
else:
return
if isinstance(obj, dict) and keys[-1] in obj:
del obj[keys[-1]]
# ========== Runtime State Management ==========
@property
def runtime_state(self) -> Dict:
"""
Return live runtime state assembled from globals (the actual source of truth).
Previously this was a static dict that was never updated, causing /config/state
to always return stale defaults. Now it reads the real values each time.
"""
import globals as g
return {
"dm_mood": getattr(g, "DM_MOOD", "neutral"),
"evil_mode": getattr(g, "EVIL_MODE", False),
"bipolar_mode": getattr(g, "BIPOLAR_MODE", False),
"language_mode": getattr(g, "LANGUAGE_MODE", "english"),
"current_gpu": self._current_gpu,
}
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
def get_state(self, key: str, default: Any = None) -> Any:
"""Get runtime state value."""
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
return self.runtime_state.get(key, default)
def set_state(self, key: str, value: Any):
"""Set runtime state value. Only current_gpu is managed here; other state lives in globals."""
if key == "current_gpu":
self._current_gpu = value
Implement comprehensive config system and clean up codebase Major changes: - Add Pydantic-based configuration system (bot/config.py, bot/config_manager.py) - Add config.yaml with all service URLs, models, and feature flags - Fix config.yaml path resolution in Docker (check /app/config.yaml first) - Remove Fish Audio API integration (tested feature that didn't work) - Remove hardcoded ERROR_WEBHOOK_URL, import from config instead - Add missing Pydantic models (LogConfigUpdateRequest, LogFilterUpdateRequest) - Enable Cheshire Cat memory system by default (USE_CHESHIRE_CAT=true) - Add .env.example template with all required environment variables - Add setup.sh script for user-friendly initialization - Update docker-compose.yml with proper env file mounting - Update .gitignore for config files and temporary files Config system features: - Static configuration from config.yaml - Runtime overrides from config_runtime.yaml - Environment variables for secrets (.env) - Web UI integration via config_manager - Graceful fallback to defaults Secrets handling: - Move ERROR_WEBHOOK_URL from hardcoded to .env - Add .env.example with all placeholder values - Document all required secrets - Fish API key and voice ID removed from .env Documentation: - CONFIG_README.md - Configuration system guide - CONFIG_SYSTEM_COMPLETE.md - Implementation summary - FISH_API_REMOVAL_COMPLETE.md - Removal record - SECRETS_CONFIGURED.md - Secrets setup record - BOT_STARTUP_FIX.md - Pydantic model fixes - MIGRATION_CHECKLIST.md - Setup checklist - WEB_UI_INTEGRATION_COMPLETE.md - Web UI config guide - Updated readmes/README.md with new features
2026-02-15 19:51:00 +02:00
logger.debug(f"📊 State: {key} = {value}")
# ========== GPU State ==========
def get_gpu(self) -> str:
"""Get current GPU selection."""
return self.get_state("current_gpu", "nvidia")
def set_gpu(self, gpu: str):
"""Set current GPU selection and persist."""
gpu = gpu.lower()
if gpu not in ["nvidia", "amd"]:
logger.warning(f"⚠️ Invalid GPU: {gpu}")
return False
# Update state
self.set_state("current_gpu", gpu)
# Persist to file
gpu_state_file = self.memory_dir / "gpu_state.json"
try:
state = {
"current_gpu": gpu,
"last_updated": datetime.now().isoformat()
}
with open(gpu_state_file, "w") as f:
json.dump(state, f, indent=2)
logger.info(f"💾 Saved GPU state: {gpu}")
return True
except Exception as e:
logger.error(f"❌ Failed to save GPU state: {e}")
return False
# ========== Configuration Export ==========
def get_full_config(self) -> Dict:
"""
Get full configuration (merged static + runtime).
Useful for API responses and debugging.
"""
return {
"static": self.static_config,
"runtime": self.runtime_config,
"state": self.runtime_state,
"merged": self._merge_configs(self.static_config, self.runtime_config)
}
def _merge_configs(self, base: Dict, override: Dict) -> Dict:
"""Deep merge two dictionaries."""
result = base.copy()
for key, value in override.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = self._merge_configs(result[key], value)
else:
result[key] = value
return result
# ========== Validation ==========
def validate_config(self) -> tuple[bool, list[str]]:
"""
Validate current configuration.
Returns:
Tuple of (is_valid, list_of_errors)
"""
errors = []
# Check required secrets
if not SECRETS.discord_bot_token or SECRETS.discord_bot_token.startswith("your_"):
errors.append("DISCORD_BOT_TOKEN not set or using placeholder")
# Validate language mode
language = self.get("discord.language_mode", "english")
if language not in ["english", "japanese"]:
errors.append(f"Invalid language_mode: {language}")
# Validate GPU
gpu = self.get_gpu()
if gpu not in ["nvidia", "amd"]:
errors.append(f"Invalid GPU selection: {gpu}")
return len(errors) == 0, errors
# ========== Global Instance ==========
# Create global config manager instance
config_manager = ConfigManager()