"""Configuration loading for Memory System API.""" from __future__ import annotations import os from pathlib import Path from typing import Any, Literal import yaml from pydantic import BaseModel, Field class ServerConfig(BaseModel): host: str = "127.0.0.1" port: int = 1934 api_key: str = "" class OpenVikingConfig(BaseModel): url: str = "http://127.0.0.1:1933" api_key: str = "" timeout: int = 30 verify_ssl: bool = True class EverOSConfig(BaseModel): url: str = "http://127.0.0.1:1995" api_key: str = "" timeout: int = 180 verify_ssl: bool = True health_path: str = "/health" class StorageConfig(BaseModel): sqlite_path: str = "/home/tom/memory-gateway/memory_system_api.sqlite3" class LoggingConfig(BaseModel): level: str = "INFO" format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" class Config(BaseModel): server: ServerConfig = Field(default_factory=ServerConfig) openviking: OpenVikingConfig = Field(default_factory=OpenVikingConfig) everos: EverOSConfig = Field(default_factory=EverOSConfig) storage: StorageConfig = Field(default_factory=StorageConfig) logging: LoggingConfig = Field(default_factory=LoggingConfig) _config: Config | None = None def load_config(config_path: str | None = None) -> Config: path = Path(config_path or os.environ.get("MEMORY_SYSTEM_CONFIG", "config.yaml")) if not path.exists(): return _apply_env_overrides(Config()) with path.open("r", encoding="utf-8") as handle: data = yaml.safe_load(handle) or {} config = Config( server=ServerConfig(**data.get("server", {})), openviking=OpenVikingConfig(**data.get("openviking", {})), everos=EverOSConfig(**data.get("everos", {})), storage=StorageConfig(**data.get("storage", {})), logging=LoggingConfig(**data.get("logging", {})), ) return _apply_env_overrides(config) def get_config() -> Config: global _config if _config is None: _config = load_config() return _config def set_config(config: Config) -> None: global _config _config = config def _apply_env_overrides(config: Config) -> Config: updates: dict[str, dict[str, Any]] = { "server": _env_updates("MEMORY_SYSTEM_SERVER", {"API_KEY": "api_key", "HOST": "host", "PORT": "port"}), "openviking": _env_updates("OPENVIKING", {"URL": "url", "BASE_URL": "url", "API_KEY": "api_key", "TIMEOUT": "timeout"}), "everos": _env_updates("EVEROS", {"URL": "url", "BASE_URL": "url", "API_KEY": "api_key", "TIMEOUT": "timeout"}), "storage": _env_updates("MEMORY_SYSTEM_STORAGE", {"SQLITE_PATH": "sqlite_path"}), } for section, values in updates.items(): if values: setattr(config, section, getattr(config, section).model_copy(update=values)) return config def _env_updates(prefix: str, mapping: dict[str, str]) -> dict[str, Any]: values: dict[str, Any] = {} for env_name, field_name in mapping.items(): raw = os.environ.get(f"{prefix}_{env_name}") if raw is None: continue values[field_name] = int(raw) if field_name in {"port", "timeout"} else raw return values