docs: add design spec for Einstellungen (settings) feature
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V57jSQPqwkGG8BuAXg59X5
This commit is contained in:
parent
dece6d838d
commit
e23ff8dca6
|
|
@ -0,0 +1,155 @@
|
||||||
|
# JARVIS Einstellungen - Design
|
||||||
|
|
||||||
|
**Datum:** 2026-09-13
|
||||||
|
**Status:** Approved, bereit fuer Implementierungsplan
|
||||||
|
|
||||||
|
## Kontext
|
||||||
|
|
||||||
|
JARVIS hat aktuell keine editierbaren Identitaets-/Branding-Werte. Der
|
||||||
|
Assistenten-Name ("JARVIS") und die Einleitung im System-Prompt
|
||||||
|
("Du bist JARVIS, ein KI-Assistent fuer Business-Automatisierung.") stehen
|
||||||
|
fest im Code bzw. als `CLAUDE_SYSTEM_PROMPT`-Env-Var-Default (`main.py:58-61`)
|
||||||
|
- letztere wird im Deployment nirgends gesetzt, ist also faktisch immer der
|
||||||
|
hartcodierte Default. Es gibt weder eine Backend-Tabelle noch eine
|
||||||
|
Frontend-Ansicht, um das zu aendern. Feature-Wunsch
|
||||||
|
(`.claude/JARVIS_FEATURES.md`, Punkt 9): ein Einstellungen-Menuepunkt mit
|
||||||
|
Werten wie E-Mailadresse, Firmenname, Name des Assistenten.
|
||||||
|
|
||||||
|
Dies ist Feature 2 von 3 einer priorisierten Roadmap (Feature 1,
|
||||||
|
Bestellungen erfassen via Nextcloud Deck, ist fertig implementiert und
|
||||||
|
deployt).
|
||||||
|
|
||||||
|
## Scope-Entscheidungen (im Brainstorming geklaert)
|
||||||
|
|
||||||
|
- **E-Mailadresse ist ein reiner Anzeige-/Branding-Wert.** Das technische
|
||||||
|
Postfach (`EMAIL_USER`/`EMAIL_PASSWORD`, IMAP/SMTP-Zugangsdaten fuer die
|
||||||
|
echten Mail-Tools) bleibt unveraendert ein Deployment-Secret in `.env` -
|
||||||
|
diese Einstellung dient nur dazu, dass Claude bei Bedarf eine
|
||||||
|
Kontakt-Adresse nennen kann, nicht um das tatsaechliche Postfach zu wechseln.
|
||||||
|
- **Speicherort: neue Postgres-Tabelle**, nicht `.env`. Aenderungen wirken
|
||||||
|
sofort auf den naechsten Chat-Request, kein Deploy/Neustart noetig.
|
||||||
|
- **Assistenten-Name wirkt auch im Frontend** (Seitentitel, Chat-Header/
|
||||||
|
Platzhalter), nicht nur im Chat-Verhalten selbst.
|
||||||
|
- **Aenderung nur ueber die Einstellungen-Seite**, kein zusaetzliches
|
||||||
|
Chat-Tool (anders als z.B. `remember_fact`) - Einstellungen werden selten
|
||||||
|
geaendert, ein dediziertes Formular reicht.
|
||||||
|
- **Bewusste Luecke:** Der Login-Screen (`Login.tsx`, `<h1>JARVIS</h1>`)
|
||||||
|
bleibt statisch. Settings liegen hinter `X-Admin-Key` wie alle anderen
|
||||||
|
`/api/v1/*`-Routen und sind vor dem Login nicht abrufbar - Branding gilt
|
||||||
|
nur fuer den eingeloggten Bereich.
|
||||||
|
|
||||||
|
## Datenmodell (`Claude outputs/migrations/007_settings.sql`)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE settings (
|
||||||
|
key VARCHAR(100) PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Reine Key-Value-Tabelle, keine Seed-Daten. Ein fester Katalog in `main.py`
|
||||||
|
definiert die gueltigen Keys und ihre Defaults:
|
||||||
|
|
||||||
|
```python
|
||||||
|
SETTINGS_DEFAULTS = {
|
||||||
|
"assistant_name": "JARVIS",
|
||||||
|
"company_name": "MBO-Tech-IT",
|
||||||
|
"contact_email": "kontakt@mbo-tech-it.de",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Fehlt ein Key in der Tabelle, gilt der Default - die Tabelle kann also leer
|
||||||
|
bleiben, bis der Nutzer zum ersten Mal etwas aendert, und das Verhalten
|
||||||
|
bleibt exakt wie heute.
|
||||||
|
|
||||||
|
## DB-Helper (analog `memory_facts`-Pattern aus main.py)
|
||||||
|
|
||||||
|
- `get_setting(key: str) -> str` - `SELECT value FROM settings WHERE key = %s`,
|
||||||
|
faellt auf `SETTINGS_DEFAULTS[key]` zurueck, wenn keine Zeile existiert.
|
||||||
|
- `get_all_settings() -> dict` - liest alle vorhandenen Zeilen, merged sie
|
||||||
|
ueber `SETTINGS_DEFAULTS` (Defaults zuerst, dann DB-Werte drueber) und
|
||||||
|
gibt ein vollstaendiges Dict mit allen drei Keys zurueck.
|
||||||
|
- `set_setting(key: str, value: str)` - `INSERT ... ON CONFLICT (key) DO
|
||||||
|
UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP`.
|
||||||
|
|
||||||
|
## REST-Endpoints
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/v1/settings -> {"assistant_name": "...", "company_name": "...", "contact_email": "..."}
|
||||||
|
PUT /api/v1/settings Body: beliebige Teilmenge der drei Keys -> aktualisiertes vollstaendiges Dict
|
||||||
|
```
|
||||||
|
|
||||||
|
Beide hinter `dependencies=[Depends(require_admin_key)]`, wie alle
|
||||||
|
bestehenden `/api/v1/*`-Routen ausser `/health`. `PUT` validiert jeden
|
||||||
|
Key gegen `SETTINGS_DEFAULTS.keys()` - unbekannter Key -> `400`.
|
||||||
|
|
||||||
|
## Einbindung in den Chat (`run_chat_completion`)
|
||||||
|
|
||||||
|
Die bisher statische Identitaets-Zeile (`CLAUDE_SYSTEM_PROMPT`-Env-Var)
|
||||||
|
wird ersetzt durch einen dynamisch aus den Settings gebauten Text, der bei
|
||||||
|
**jedem** Chat-Aufruf frisch berechnet wird (gleiches Prinzip wie der
|
||||||
|
bestehende `_current_datetime_context()`):
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def _identity_system_prompt() -> str:
|
||||||
|
settings = await get_all_settings()
|
||||||
|
return (
|
||||||
|
f"Du bist {settings['assistant_name']}, der KI-Assistent von "
|
||||||
|
f"{settings['company_name']}. Bei Fragen zur Erreichbarkeit kannst "
|
||||||
|
f"du auf {settings['contact_email']} verweisen."
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Die `CLAUDE_SYSTEM_PROMPT`-Env-Var und ihr Default-Text entfallen komplett
|
||||||
|
(im Deployment ohnehin nirgends gesetzt) - die Settings-Tabelle ist ab jetzt
|
||||||
|
die einzige Quelle fuer die Identitaets-Zeile.
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
- Neue Komponente `web/src/components/Settings.tsx`: Formular mit drei
|
||||||
|
Textfeldern (Assistentenname, Firmenname, Kontakt-E-Mail) +
|
||||||
|
"Speichern"-Button. Laedt aktuelle Werte per `GET /api/v1/settings` beim
|
||||||
|
Mounten (Pattern wie `Dashboard.tsx`), speichert per `PUT`.
|
||||||
|
- `App.tsx`: `View`-Type um `"settings"` erweitert, neuer Nav-Button
|
||||||
|
"Einstellungen". Laedt die Settings einmalig nach dem Login, setzt
|
||||||
|
`document.title` auf `assistant_name` und reicht den Namen als Prop an
|
||||||
|
`Chat.tsx` durch, das damit den bisher festen Platzhaltertext
|
||||||
|
("Nachricht an JARVIS...") ersetzt.
|
||||||
|
|
||||||
|
## Fehlerbehandlung
|
||||||
|
|
||||||
|
- `GET /api/v1/settings` schlaegt fehl (DB nicht erreichbar): Frontend
|
||||||
|
zeigt eine Fehlermeldung im Settings-Formular (gleiches Muster wie
|
||||||
|
`CalendarWidget`/`EmailWidget`); `App.tsx` faellt beim Titel/Platzhalter
|
||||||
|
auf den hartcodierten Default ("JARVIS") zurueck, kein harter Fehler.
|
||||||
|
- `PUT` mit unbekanntem Key: `400`, Formular zeigt die Fehlermeldung an,
|
||||||
|
nichts wird gespeichert (auch nicht die gueltigen Keys aus demselben
|
||||||
|
Request - alles oder nichts, kein Teil-Update bei Validierungsfehlern).
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Unit-Tests (`tests/test_settings.py`, gemockte DB wie bei bestehenden
|
||||||
|
Tool-Tests) fuer `get_setting`/`get_all_settings`/`set_setting`
|
||||||
|
(Default-Fallback, Merge-Verhalten, Upsert) und die REST-Endpoints
|
||||||
|
(Erfolg, unbekannter Key -> 400, fehlender Admin-Key -> 401).
|
||||||
|
- Test fuer `run_chat_completion`: `_identity_system_prompt()` wird
|
||||||
|
gemockt, Ergebnis muss im `system`-Parameter des Claude-Aufrufs landen
|
||||||
|
(gleiches Pattern wie der bestehende Datums-Kontext-Test).
|
||||||
|
- Frontend: kein neuer Komponenten-Test (Projekt-Konvention, siehe
|
||||||
|
`CalendarWidget`/`EmailWidget`/`OrdersWidget`), Verifikation ueber
|
||||||
|
`npm run build`.
|
||||||
|
- Manueller End-to-End-Test nach Deployment: Einstellungen-Seite oeffnen,
|
||||||
|
Assistentenname aendern und speichern, pruefen dass Seitentitel und
|
||||||
|
Chat-Platzhalter sich sofort aktualisieren, im Chat fragen "wie heisst
|
||||||
|
du?" und pruefen, dass die Antwort den neuen Namen nutzt.
|
||||||
|
|
||||||
|
## Out of Scope (bewusst nicht Teil dieser Phase)
|
||||||
|
|
||||||
|
- Aenderung der Einstellungen per Chat-Tool (siehe Scope-Entscheidungen).
|
||||||
|
- Aendern des technischen Postfachs (`EMAIL_USER`/`EMAIL_PASSWORD`) ueber
|
||||||
|
die UI - bleibt Deployment-Secret.
|
||||||
|
- Dynamisches Branding auf dem Login-Screen (siehe "bewusste Luecke" oben).
|
||||||
|
- Weitere Einstellungen ueber die drei genannten hinaus (die Tabelle ist
|
||||||
|
aber generisch genug, dass spaetere Erweiterung nur einen neuen Eintrag
|
||||||
|
in `SETTINGS_DEFAULTS` + ein Formularfeld braucht, keine Schema-Aenderung).
|
||||||
Loading…
Reference in New Issue