import json from dataclasses import dataclass, field from pathlib import Path @dataclass class Config: daemon: dict = field(default_factory=lambda: {"hotkey": "Cmd+m"}) recording: dict = field(default_factory=lambda: {"input": ""}) stt: dict = field(default_factory=lambda: {"model": "base", "device": "cpu"}) injection: dict = field(default_factory=lambda: {"backend": "clipboard"}) ai_cleanup: dict = field( default_factory=lambda: { "model": "llama3.2:3b", "base_url": "http://localhost:11434", "api_key": "", } ) def default_path() -> Path: return Path.home() / ".config" / "lel" / "config.json" def load(path: str | None) -> Config: cfg = Config() p = Path(path) if path else default_path() if p.exists(): data = json.loads(p.read_text(encoding="utf-8")) if any(k in data for k in ("daemon", "recording", "stt", "injection", "ai_cleanup")): for k, v in data.items(): if hasattr(cfg, k): setattr(cfg, k, v) else: cfg.daemon["hotkey"] = data.get("hotkey", cfg.daemon["hotkey"]) cfg.recording["input"] = data.get("input", cfg.recording["input"]) cfg.stt["model"] = data.get("whisper_model", cfg.stt["model"]) cfg.stt["device"] = data.get("whisper_device", cfg.stt["device"]) cfg.injection["backend"] = data.get("injection_backend", cfg.injection["backend"]) cfg.ai_cleanup["model"] = data.get("ai_model", cfg.ai_cleanup["model"]) cfg.ai_cleanup["base_url"] = data.get("ai_base_url", cfg.ai_cleanup["base_url"]) cfg.ai_cleanup["api_key"] = data.get("ai_api_key", cfg.ai_cleanup["api_key"]) if not isinstance(cfg.daemon, dict): cfg.daemon = {"hotkey": "Cmd+m"} if not isinstance(cfg.recording, dict): cfg.recording = {"input": ""} if not isinstance(cfg.stt, dict): cfg.stt = {"model": "base", "device": "cpu"} if not isinstance(cfg.injection, dict): cfg.injection = {"backend": "clipboard"} if not isinstance(cfg.ai_cleanup, dict): cfg.ai_cleanup = { "model": "llama3.2:3b", "base_url": "http://localhost:11434", "api_key": "", } validate(cfg) return cfg def redacted_dict(cfg: Config) -> dict: d = cfg.__dict__.copy() if isinstance(d.get("ai_cleanup"), dict): d["ai_cleanup"] = d["ai_cleanup"].copy() d["ai_cleanup"]["api_key"] = "" return d def validate(cfg: Config) -> None: if not cfg.daemon.get("hotkey"): raise ValueError("daemon.hotkey cannot be empty")