diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md index 1473538..9897198 100644 --- a/BEDIENUNGSANLEITUNG.md +++ b/BEDIENUNGSANLEITUNG.md @@ -793,7 +793,7 @@ https://va.beispiel.de/ ← remote über Reverse-Proxy (alles, inkl. Mikr | Element | Funktion | |---------|----------| | **👄 / Titel** | Marke; Mund-Symbol als Wiedererkennung | -| **Sprache ▾** | Antwortsprache (→ § 6.6): `🔄 Flex` = folgt der gesprochenen Sprache · feste Sprache (🇩🇪/🇬🇧/…) = Antwort + Stimme in dieser Sprache | +| **Sprache ▾** | Antwortsprache (fest, → § 6.6). Diktat in einer Fremdsprache wird automatisch in die Zielsprache übersetzt. Welche Sprachen erscheinen, legt der Admin pro Nutzer fest. | | **⋮ Menü** | Öffnet die Einstellungen (unten) | **Identitätsleiste (direkt unter der Kopfzeile):** links „Angemeldet als …" (SSO-Identität; @@ -1395,52 +1395,44 @@ print(result) # → 'Chluteur kommt.' --- -### 6.6 Sprache wechseln (Fix / Flex) +### 6.6 Sprache (fest, pro Nutzer) -Die Antwortsprache wird in der Web-Oberfläche über **ein einziges Dropdown** („Sprache") -gesteuert. Es kennt zwei Arten von Werten: +Die Sprache ist **immer fest** — den früheren „Flex"-Modus gibt es nicht mehr. Im Dropdown +„Sprache" wählt man eine konkrete Sprache; **Antwort und vorlesende Stimme sind immer in +dieser Sprache.** -| Auswahl | Bedeutung | Whisper (STT) | LLM-Antwort + Stimme | -|---------|-----------|---------------|----------------------| -| **🔄 Flex** | keine feste Sprache — folgt automatisch der gesprochenen | erkennt die Sprache, **übersetzt nicht** | in der **erkannten** Sprache (blaue Blase zeigt das Original) | -| **🇩🇪 🇬🇧 🇫🇷 🇪🇸 🇮🇹 🇧🇷 🇵🇱 🇸🇦 🇷🇺 🇨🇳** | „Fix": System bleibt bei dieser Sprache | bekommt die feste Sprache → **übersetzt** die Eingabe | immer in der **festen** Sprache, egal worin gefragt wurde | +**Fremdsprache diktieren → Übersetzung:** Spricht man in einer **anderen** als der +eingestellten Sprache, erkennt das System die gesprochene Sprache automatisch und +**übersetzt die Anfrage in die Zielsprache**. Sie wird dann auch in der Zielsprache +angezeigt und beantwortet — hilfreich beim Sprachenlernen und über Sprachgrenzen hinweg. -**Beispiele** (Eingabe auf Französisch gesprochen): +Beispiel (Zielsprache 🇩🇪 DE, auf Französisch gesprochen): Die Blase zeigt die **deutsche** +Übersetzung; Antwort + Stimme sind Deutsch. -- **Flex** → blaue Blase: französischer Originaltext · Antwort + Stimme: Französisch. -- **🇩🇪 DE** → blaue Blase: deutsche Übersetzung · Antwort + Stimme: Deutsch. +Die vorlesende Piper-Stimme folgt der Sprache automatisch; die ♂/♀-Buttons bestimmen die +Variante (→ `LANG_TO_PIPER_VOICE`, `PIPER_VOICE_GENDERED`). -Die vorlesende Piper-Stimme folgt immer der Antwortsprache automatisch (z. B. `thorsten`/`kerstin` -für Deutsch, `siwis`/`tom` für Französisch — Zuordnung → `LANG_TO_PIPER_VOICE` und -`PIPER_VOICE_GENDERED`). Die aktive Geschlechtspräferenz (♂/♀-Buttons) bestimmt, welche -Variante gewählt wird. Bei Text-Chat -im Flex-Modus (keine Audio-Erkennung möglich) bekommt das LLM **keine** Sprachvorgabe und -antwortet von selbst in der Sprache der Eingabe; als Fallback gilt `DEFAULT_LANGUAGE`. - -> **Hinweis:** Es gibt kein separates „Fix/Flex"-Menü mehr — eine konkrete Sprache zu -> wählen *ist* der Fix-Modus, „🔄 Flex" der flexible. Intern bleiben zwei Felder -> erhalten: `language` (ISO-Code) und `language_mode` (`fix` | `flex`). +**Erlaubte Sprachen pro Nutzer (Admin):** Im Admin → **Nutzer** legt der Admin per +Sprach-Chips fest, welche Sprache(n) ein Nutzer wählen darf. Die App zeigt dann nur diese; +bei **genau einer** erlaubten Sprache verschwindet das Sprachmenü ganz (kein Stress). **Konfiguration außerhalb der Web-UI:** ```bash -DEFAULT_LANGUAGE=de # global in .env (Fallback-/Standardsprache) -DEFAULT_LANGUAGE_MODE=fix # global: fix | flex (Admin: Tab „⚙ Einstellungen") +DEFAULT_LANGUAGE=de # globale Standardsprache (Admin: „⚙ Konfiguration") ``` Pro Nutzer (dauerhaft): ```bash -# Feste Sprache (Fix): +# Standardsprache des Nutzers: curl -s -X PUT $URL/api/me/prefs -H "Authorization: Bearer $TOKEN" \ - -H 'Content-Type: application/json' -d '{"language":"en","language_mode":"fix"}' | jq + -H 'Content-Type: application/json' -d '{"language":"en"}' | jq -# Flex (Sprache folgt automatisch): -curl -s -X PUT $URL/api/me/prefs -H "Authorization: Bearer $TOKEN" \ - -H 'Content-Type: application/json' -d '{"language_mode":"flex"}' | jq +# Erlaubte Sprachen setzt der Admin (CSV, leer = alle): +curl -s -X PUT $URL/api/admin/users/$USER_ID/prefs -H "X-Admin-Key: $ADMIN_KEY" \ + -H 'Content-Type: application/json' -d '{"allowed_languages":"de,en"}' | jq ``` -Pro Aufruf: `{"text":"…","language":"en","language_mode":"fix"}` im Body. - --- ### 6.7 Audio-Geräte (Mikrofon, Lautsprecher, Bluetooth) @@ -2006,34 +1998,48 @@ Pro Nutzer übersteuern: `daily_request_limit` in `PUT /api/me/prefs`. --- -## 10. Notfall-Erkennung und Eskalation +## 10. Notruf (vom Nutzer ausgelöst) > 🔧 Admin -Das System erkennt Notlagen-Signale (medizinisch, Sturz, Hilferuf) in der Nutzereingabe. -Die Erkennung ist **zweistufig**: +Es gibt **keine automatische** Notfallerkennung mehr (früher Stichwörter + LLM) — die war +unzuverlässig (Fehlalarme **wie** verpasste Notfälle) und intransparent. Ein Notruf entsteht +**nur durch eine bewusste Nutzeraktion**: -1. **Stichwort-Heuristik** — im Hot-Path, sofort, ohne Latenz. -2. **LLM-Klassifikation** — läuft als Hintergrund-Task, **nur** wenn Stufe 1 nichts fand. - Erkennt verpasste Formulierungen (metaphorische Suizidalität, Schlaganfall-Symptome - ohne Schlüsselwort) mit Konfidenz-Schwelle. +- In der App oben links ein dezenter roter **🆘-Knopf** („Hilfe rufen"). +- Tippen → Rückfrage „Wirklich Hilfe rufen?" → **erst nach Bestätigung** wird ausgelöst. +- Der Notruf wird protokolliert (Admin → System → **Notrufe**); der Nutzer erhält einen + klaren Hinweis in seiner Sprache. -Bei Treffer: Protokolleintrag + Metrik + optionaler Webhook + Signal im Response -(`X-Emergency`-Header, `emergency`-Feld, WebSocket-`emergency`-Event). +**Benachrichtigung (Testphase):** Ist SMTP konfiguriert, geht beim Notruf eine **E-Mail** +an die Kontaktperson(en) (`EMERGENCY_CONTACT_EMAIL`). Ohne SMTP wird nichts versendet und +der Nutzer sieht „ACHTUNG: Es ist noch keine Benachrichtigung eingebaut.". SMS/Anruf folgen +später. ```bash -EMERGENCY_WEBHOOK_URL=https://example.org/alert # optional -EMERGENCY_LLM_ENABLED=true # Stufe 2 (Standard: an) -EMERGENCY_LLM_MIN_CONFIDENCE=0.6 # Schwelle gegen Fehlalarme -EMERGENCY_LLM_PROVIDER= # leer = Default-LLM +EMERGENCY_CONTACT_EMAIL=dieter.schlueter@linix.de # Default-Kontaktperson +SMTP_HOST= # ohne Host kein Versand — KEINE Inline-Kommentare hinter Werten! +SMTP_PORT=587 +SMTP_USER= +SMTP_PASSWORD= +SMTP_FROM= # muss dem SMTP_USER gehören (sonst 553 Sender rejected) +SMTP_STARTTLS=true +EMERGENCY_WEBHOOK_URL= # optional, für spätere Eskalation ``` -> ⚠️ **Wichtig:** Die Erkennung ist **keine verlässliche Lebensrettung** und kein Ersatz -> für einen echten Notruf. Sie kann Notlagen verpassen oder Fehlalarme auslösen. -> Erkannte Texte sind hochsensibel (DSGVO Art. 9: Einwilligung, Aufbewahrung, Zugriff). +Auslösen per API (z. B. zum Testen): +```bash +curl -s -X POST $URL/api/emergency -H 'Content-Type: application/json' \ + -d '{"language":"de"}' | jq +# → {"category":"manual","notice":"…","email_sent":true|false} +``` + +> ⚠️ **Wichtig:** Der Notruf ist **kein Ersatz** für einen echten Rettungsdienst. In der +> Testphase ist die Zustellung nur E-Mail (best effort, abhängig vom Mailserver). --- + ## 11. Remote-Zugang und Deployment > 🔧 Admin @@ -2283,10 +2289,13 @@ Logs: Terminal von `make run`. Mehr Details: `LOG_LEVEL=debug` in `.env`. | Variable | Default | Bedeutung | |----------|---------|-----------| | `DAILY_REQUEST_LIMIT` | `0` | Anfragen/Nutzer/Tag (0 = unbegrenzt) | -| `EMERGENCY_WEBHOOK_URL` | (leer) | Webhook-URL für Notfall-Eskalation | -| `EMERGENCY_LLM_ENABLED` | `true` | LLM-Klassifikation (Stufe 2) ein/aus | -| `EMERGENCY_LLM_PROVIDER` | (leer = Default-LLM) | Provider für Klassifikation | -| `EMERGENCY_LLM_MIN_CONFIDENCE` | `0.6` | Konfidenz-Schwelle gegen Fehlalarme | +| `EMERGENCY_CONTACT_EMAIL` | `dieter.schlueter@linix.de` | Kontaktperson für die Notruf-E-Mail | +| `SMTP_HOST` | (leer) | Mailserver für Notruf-E-Mail (leer = kein Versand) | +| `SMTP_PORT` | `587` | SMTP-Port | +| `SMTP_USER` / `SMTP_PASSWORD` | (leer) | SMTP-Zugangsdaten | +| `SMTP_FROM` | (leer → `SMTP_USER`) | Absender (muss dem SMTP_USER gehören) | +| `SMTP_STARTTLS` | `true` | STARTTLS verwenden | +| `EMERGENCY_WEBHOOK_URL` | (leer) | optionaler Webhook (spätere Eskalation) | --- @@ -2316,8 +2325,7 @@ Wichtige Body-Felder für `/api/chat` und `/api/speak`: | Feld | Typ | Bedeutung | |------|-----|-----------| | `text` | string | Eingabe-Text (Pflicht) | -| `language` | string | Sprache, z. B. `de`, `en` (im Fix-Modus maßgeblich) | -| `language_mode` | string | `fix` (feste Sprache, Eingabe wird übersetzt) oder `flex` (folgt der erkannten Sprache) — → § 6.6 | +| `language` | string | Zielsprache, z. B. `de`, `en` — Antwort + Stimme (→ § 6.6) | | `stt_provider` | string | Provider für diese Anfrage | | `llm_provider` | string | Provider für diese Anfrage | | `tts_provider` | string | Provider für diese Anfrage | @@ -2340,7 +2348,7 @@ Body-Felder: `input_endpoint`, `output_endpoint`, `stt_provider`, `llm_provider` | Methode | Pfad | Beschreibung | |---------|------|--------------| | `GET` | `/api/me` | Aktueller Nutzer + Präferenzen | -| `PUT` | `/api/me/prefs` | Dauerhafte Routing-Präferenzen setzen (Merge; Felder u. a. `language`, `language_mode`, `tts_provider` — → § 6.6) | +| `PUT` | `/api/me/prefs` | Dauerhafte Nutzer-Präferenzen setzen (Merge; Felder u. a. `language`, `tts_provider` — → § 6.6) | | `GET` | `/api/me/memories` | Alle Langzeit-Erinnerungen | | `POST` | `/api/me/memories` | Erinnerung hinzufügen | | `DELETE` | `/api/me/memories/{id}` | Erinnerung löschen |