feat(admin): Phase 3 — Laufzeit-Konfiguration ohne Server-Neustart
Neue Tabelle config_overrides in SQLite; RuntimeSettings-Wrapper liest
überschreibbare Felder mit 30s TTL-Cache aus der DB und fällt auf
.env-Werte zurück. dependencies.py und quota.py nutzen runtime_settings
als Default statt des statischen Settings-Singletons.
16 Felder überschreibbar: STT/LLM/TTS-Provider, LLM-Modelle, Stimmen,
Systemprompt, Temperatur, Tageskontingent, Normalisierung u.a.
Backend: GET/PUT/DELETE /api/admin/config/{key}
Admin-UI: neuer Tab "⚙ Einstellungen" mit Inline-Edit und Reset.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
296138ac1e
commit
5d022aaf63
7 changed files with 285 additions and 9 deletions
32
app/store.py
32
app/store.py
|
|
@ -152,6 +152,15 @@ class Store(ABC):
|
|||
@abstractmethod
|
||||
def get_all_usage(self) -> list[dict]: ...
|
||||
|
||||
@abstractmethod
|
||||
def get_config_overrides(self) -> dict[str, str]: ...
|
||||
|
||||
@abstractmethod
|
||||
def set_config_override(self, key: str, value: str) -> None: ...
|
||||
|
||||
@abstractmethod
|
||||
def delete_config_override(self, key: str) -> bool: ...
|
||||
|
||||
|
||||
class SQLiteStore(Store):
|
||||
def __init__(self, db_path: str):
|
||||
|
|
@ -216,6 +225,11 @@ class SQLiteStore(Store):
|
|||
snippet TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS config_overrides (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
"""
|
||||
)
|
||||
# Migration fuer bestehende DBs: external_id ergaenzen (falls noch nicht da).
|
||||
|
|
@ -551,3 +565,21 @@ class SQLiteStore(Store):
|
|||
" GROUP BY u.user_id ORDER BY total_requests DESC",
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
def get_config_overrides(self) -> dict[str, str]:
|
||||
with self._connect() as conn:
|
||||
rows = conn.execute("SELECT key, value FROM config_overrides").fetchall()
|
||||
return {r["key"]: r["value"] for r in rows}
|
||||
|
||||
def set_config_override(self, key: str, value: str) -> None:
|
||||
with self._connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO config_overrides(key, value, updated_at) VALUES(?,?,?)"
|
||||
" ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at",
|
||||
(key, value, _now()),
|
||||
)
|
||||
|
||||
def delete_config_override(self, key: str) -> bool:
|
||||
with self._connect() as conn:
|
||||
cur = conn.execute("DELETE FROM config_overrides WHERE key=?", (key,))
|
||||
return cur.rowcount > 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue