2026-06-01 20:06:22 +02:00
|
|
|
"""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
|
2026-06-01 21:28:29 +02:00
|
|
|
ai_search_extra_hints: str = ""
|
2026-06-01 20:06:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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),
|
2026-06-01 21:28:29 +02:00
|
|
|
ai_search_extra_hints=_get_value(rows, "ai_search_extra_hints", ""),
|
2026-06-01 20:06:22 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
2026-06-01 21:28:29 +02:00
|
|
|
ai_search_extra_hints: str | None = None,
|
2026-06-01 20:06:22 +02:00
|
|
|
) -> 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()
|
2026-06-01 21:28:29 +02:00
|
|
|
if ai_search_extra_hints is not None:
|
|
|
|
|
updates["ai_search_extra_hints"] = ai_search_extra_hints
|
2026-06-01 20:06:22 +02:00
|
|
|
|
|
|
|
|
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()
|