ADR-019: Runtime User Preferences via DB Override Table¶
Status¶
Accepted
Context¶
WikiMind loads all configuration from .env / environment variables via Pydantic BaseSettings with an @lru_cache singleton. This means changing the default LLM provider, monthly budget, or fallback toggle requires editing .env and restarting the app.
The Settings UI (#29) needs to let users change a small set of frequently-toggled values at runtime without restarting. mcp-context-forge solves this with database-backed runtime config — provider state lives in DB tables, toggled via API, committed immediately.
Decision¶
Introduce a UserPreference key-value table for runtime overrides. Only three settings are runtime-changeable:
| Key | Type | Default |
|---|---|---|
llm.default_provider | str | "anthropic" |
llm.monthly_budget_usd | float | 50.0 |
llm.fallback_enabled | bool | true |
Precedence: DB row wins if it exists. Otherwise falls back to .env defaults.
Startup: After init_db(), _apply_db_preferences() reads all UserPreference rows and applies them to the in-memory Settings singleton. This ensures DB overrides survive restarts.
Write path: API endpoints (POST /settings/llm/default-provider, PATCH /settings) write to the DB table AND mutate the in-memory singleton for immediate effect.
Read path: GET /settings overlays DB values onto the response.
Consequences¶
- Users can change default provider, budget, and fallback from the UI without restart.
.envremains the canonical source for infrastructure config (host, port, data_dir, API keys).- The
user_preferencetable is lightweight (max 3 rows). No schema migration needed — auto-created by SQLModel. - Deleting the DB reverts all preferences to
.envdefaults cleanly. - Budget warning thresholds (
budget_warning_pct,budget_check_cache_seconds) remain.env-only — they are operational tuning knobs, not user-facing settings.