"""Settings read/write helpers for the ``app_settings`` KV table. Provides a typed ``LLMConfig`` dataclass and two helpers: - ``get_app_settings(db) -> LLMConfig`` — reads KV rows (or returns defaults). - ``save_app_settings(db, ...) -> None`` — writes KV rows; API key left blank means "keep the old value". """ from __future__ import annotations from dataclasses import dataclass from sqlalchemy.orm import Session from app.models import AppSetting @dataclass class LLMConfig: """All settings consumed by the LLM client and settings UI.""" enabled: bool = False base_url: str = "https://api.openai.com/v1" model: str = "" api_key: str = "" ai_search_enabled: bool = False def _get_value(rows: dict[str, str], key: str, default: str) -> str: return rows.get(key, default) def _get_bool(rows: dict[str, str], key: str, default: bool) -> bool: return rows.get(key, str(default).lower()) == "true" def get_app_settings(db: Session) -> LLMConfig: """Read all settings from ``app_settings`` and return an ``LLMConfig``.""" rows: dict[str, str] = {} for row in db.query(AppSetting).all(): if row.value is not None: rows[row.key] = row.value return LLMConfig( enabled=_get_bool(rows, "llm_enabled", False), base_url=_get_value(rows, "llm_base_url", "https://api.openai.com/v1"), model=_get_value(rows, "llm_model", ""), api_key=_get_value(rows, "llm_api_key", ""), ai_search_enabled=_get_bool(rows, "ai_search_enabled", False), ) def save_app_settings( db: Session, *, enabled: bool | None = None, base_url: str | None = None, model: str | None = None, api_key: str | None = None, ai_search_enabled: bool | None = None, ) -> None: """Write settings to ``app_settings``. If ``api_key`` is ``None`` (form field left blank), the existing key is preserved. All other fields are written as-is. """ updates: dict[str, str | None] = {} if enabled is not None: updates["llm_enabled"] = str(enabled).lower() if base_url is not None: updates["llm_base_url"] = base_url if model is not None: updates["llm_model"] = model if api_key is not None: updates["llm_api_key"] = api_key if ai_search_enabled is not None: updates["ai_search_enabled"] = str(ai_search_enabled).lower() for key, value in updates.items(): existing = db.get(AppSetting, key) if existing is not None: existing.value = value else: db.add(AppSetting(key=key, value=value)) db.commit()