"""FlowDeck — Runtime LLM configuration store (v4.10.1). Precedence: a saved `llm_config` DB row (id=1) overrides the .env settings. The row is created only once an admin saves the LLM settings from the UI; until then the app falls back to `settings.llm_*` (default: offline mock). """ from __future__ import annotations from app.config import settings from app.services.llm_client import PROVIDERS, PROVIDER_MODELS __all__ = ["get_llm_config", "set_llm_config", "provider_info"] def get_llm_config() -> dict: """Return the effective LLM config — DB overrides .env when present.""" cfg = { "provider": settings.llm_provider or "offline", "model": settings.llm_model or "gpt-4o", "api_key": settings.llm_api_key or "", "api_base": settings.llm_api_base or "", } try: from app.db import get_conn with get_conn() as conn: row = conn.execute( "SELECT provider, model, api_key, api_base FROM llm_config WHERE id=1" ).fetchone() except Exception: # noqa: BLE001 — DB not ready yet → env defaults return cfg if row: for key in ("provider", "model", "api_key", "api_base"): if row[key]: cfg[key] = row[key] return cfg def set_llm_config(*, provider: str | None = None, model: str | None = None, api_key: str | None = None, api_base: str | None = None) -> dict: """Upsert the runtime LLM config row (id=1) and return the new effective config.""" from app.db import get_conn cfg = get_llm_config() if provider is not None: cfg["provider"] = provider if model is not None: cfg["model"] = model if api_key is not None: cfg["api_key"] = api_key if api_base is not None: cfg["api_base"] = api_base with get_conn() as conn: conn.execute( """INSERT INTO llm_config (id, provider, model, api_key, api_base, updated_at) VALUES (1, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(id) DO UPDATE SET provider=excluded.provider, model=excluded.model, api_key=excluded.api_key, api_base=excluded.api_base, updated_at=CURRENT_TIMESTAMP""", (cfg["provider"], cfg["model"], cfg["api_key"], cfg["api_base"]), ) conn.commit() return cfg def provider_info() -> list[dict]: """Providers list for the UI: known models + whether an API key is required.""" out: list[dict] = [] for name, (_base, default_model) in PROVIDERS.items(): models = list(PROVIDER_MODELS.get(name) or ()) if default_model and default_model not in models: models.insert(0, default_model) out.append({ "id": name, "name": name.capitalize(), "default_model": default_model, "models": models, "requires_key": name not in ("offline", "ollama"), }) return out