Commit graph

155 commits

Author SHA1 Message Date
Dieter Schlüter
487c8aa3c1 feat(admin): Redesign — 5 Bereiche + Übersicht-Dashboard + Sub-Navigation
Von 8 gemischten Tabs auf 5 logische Bereiche verdichtet:
- 📊 Übersicht (neu, Start): Kennzahlen (Nutzer, Anfragen, Notfälle) + LLM-Backend/GPU
  + Schnell-Sprünge.
- 👥 Nutzer: Sub-Tabs Verwalten + Gespräche.
- 🚨 Notfälle.
- 🖥 System: Sub-Tabs Status (inkl. LLM-Steuerung) + Metriken + Log.
- ⚙ Konfiguration: Sub-Tabs Einstellungen + Wörterbuch.

- Sub-Navigation (Segmented-Buttons) für Bereiche mit mehreren Ansichten.
- Bestehende Content-Blöcke + Loader unverändert wiederverwendet (nur neu gebündelt)
  -> geringes Risiko. loadOverview neu (vorhandene Endpunkte).
- Doku §7.5 auf 5-Bereiche-Struktur aktualisiert; Wörterbuch-Hinweis (alle 8 Sprachen,
  Zeile-anklicken-bearbeiten) korrigiert.

167 grün, JS-Syntax ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:11:57 +02:00
Dieter Schlüter
35fd3a5d15 fix(ui): Senden als runder Icon-Button (Fußzeile überlaufsicher)
- Senden ➤ als runder Icon-Button (gleich groß wie Mikrofon) statt Text -> passt
  auch im Querformat/schmalen Geräten, kein Abschneiden mehr.
- Eingabefeld min-w-0, Buttons shrink-0 -> Reihe kann nicht überlaufen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 23:04:35 +02:00
Dieter Schlüter
3f6bf53a7f refactor(ui): Geräte-STT entfernt + Menü-Redesign (schlanke Kopfzeile + ⋮-Sheet)
Geräte-STT (Web Speech API) entfernt — auf Android nur Cloud, Qualität < Whisper,
verwirrend. STT läuft jetzt überall serverseitig (Whisper). Geräte-TTS bleibt.
- Backend: allow_cloud_stt (config/runtime_config/api me) + Tests entfernt.
- Frontend: SpeechRecognition-Code, STT-Dropdown, "Lokal"-Button raus.

Menü-Redesign (verständlicher + passt auf Handys):
- Kopfzeile schlank: Mund-Icon + Titel + Sprache + ⋮-Menübutton (kein Überlauf mehr).
- ⋮ öffnet ein Einstellungs-Sheet: Ton als 3 Presets (📱 Im Gerät / ☁ Server /
   Beste Qualität) statt zweier kryptischer "Gerät"-Dropdowns; Neues Gespräch;
  Tag-/Nachtmodus; Konto (Identität, Admin, Abmelden).
- #tts bleibt als verborgenes Quell-Select -> bestehende TTS-Logik unverändert.
- Doku §5.1.2 neu, §6.3 STT-Hinweis. 167 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:26:20 +02:00
Dieter Schlüter
f74d2fadb2 fix(stt): iOS-Live-Vorschau wiederhergestellt + klare Server-Fallback-Meldung
- resolveSttMode behandelt iOS-Safari (keine available()-API) wieder als "local"
  -> on-device SpeechRecognition mit Live-Interim funktioniert auf iPhone wieder.
- Server-Fallback zeigt jetzt "Server-Erkennung. Live-Vorschau nur mit lokalem/
  Cloud-STT" (startRecording optionaler Status-Text) statt generischem Hinweis.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:28:39 +02:00
Dieter Schlüter
dc597bd77b fix(stt): processLocally-Feinschliff (Android-Bug "language not supported") + "Alles lokal"
- Geräte-STT prüft vor lokaler Erkennung das On-Device-Sprachpaket via
  SpeechRecognition.available(); fehlt es, install() im Hintergrund + Fallback auf
  Cloud (falls erlaubt) bzw. Server. processLocally wird nur bei "available" gesetzt
  -> behebt "language not supported" auf Android-Chrome.
- onerror/Start-Fehler (language-not-supported/network/service) -> sauberer
  Server-STT-Fallback statt Fehlermeldung.
- Schnellwahl "📴 Lokal": setzt STT + TTS gemeinsam auf Gerät (nur Text).
- Doku §6.3.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:32:17 +02:00
Dieter Schlüter
ab9c4f4938 feat(stt): Geräte-STT auf Mobilgeräten (Web Speech API) — getrennt, Datenschutz-Linie C
- Geräte-STT erkennt Sprache lokal und sendet nur Text über den Text-Turn; spart
  Audio-Upload + Server-STT. Getrennter Schalter (STT ▾) unabhängig vom TTS.
- Linie C: nur bei nachweislich lokaler Erkennung (iOS / Chrome on-device); Cloud
  (z. B. Chrome-Desktop -> Google) nur mit Admin-Flag ALLOW_CLOUD_STT.
  -> config.allow_cloud_stt, RUNTIME_SETTABLE, /api/me, Admin-Toggle.
- Fix-only (SpeechRecognition braucht Sprach-Hint); Flex -> Server-STT-Fallback.
  Kein/instabiles SpeechRecognition (z. B. Firefox) -> Server-STT. Live-Interim im
  Eingabefeld. Terminal/Desktop/Laptop unverändert serverseitig (Option nur sichtbar,
  wenn das Gerät lokale Erkennung bietet).
- Tests: /api/me-Flag + runtime-setzbar (169 grün). Doku §5.1.2 + §6.3.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:08:10 +02:00
Dieter Schlüter
33d7189418 feat(admin): Audit-Logging schreibender Admin-Aktionen — Plan-Schritt 5
- app/audit.py: eigener Logger "va.audit" (eigener stdout-Handler -> Journal/Log-Tab).
  log_admin_action() protokolliert Aktion + Auslöser (SSO-Name oder admin-key).
- Verdrahtet in: config_set/config_reset (PUT/DELETE /admin/config),
  llm_backend_switch (+ _rejected), gateway_restart.
- Tests: caplog prüft Audit-Zeilen für config_set und abgelehnten Backend-Switch.
- Doku §7.5: Audit-Format im Log-Tab.

Schließt den Plan (Admin-gesteuerte LLM-/Gateway-Verwaltung, Schritte 1–5) ab. 167 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:50:55 +02:00
Dieter Schlüter
b2576fd465 feat(llm): Live-Parameter top_p + Live/Restart-Kennzeichnung — Plan-Schritt 4
- local_llm_top_p (Default 0.9) durch Config -> runtime_config (RUNTIME_SETTABLE,
  live ohne Neustart) -> local-openai-compatible Payload (top_p).
- Admin-Einstellungen: top_p als Slider (0–1); Hints "wirkt sofort (kein Neustart)"
  für temperature/top_p.
- Doku: top_p + Klarstellung Live-Parameter (temp/top_p/max_tokens) vs.
  Startup-Wert Kontextfenster (OLLAMA_CONTEXT_LENGTH / llama.cpp -c).

Verifiziert: top_p im Payload, Live-Config-Round-Trip (PUT/DELETE). 165 grün.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:46:33 +02:00
Dieter Schlüter
5b6f0f8ef2 feat(admin): Backend-Wechsel + Gateway-Neustart aus dem Admin-Panel — Plan-Schritt 3
- admin_llm: switch_backend() mit strikter Allowlist (backend ∈ {ollama,llamacpp},
  Modell gegen 'ollama list' + Format-Regex), detached (Self-Restart-sicher),
  niemals shell=True. restart_gateway_detached() für systemd-User-Dienst.
- switch-llm.sh: flock-Lock gegen parallele Backend-Wechsel (Exit 75).
- Endpunkte POST /api/admin/llm/backend (422 bei ungültig) und
  POST /api/admin/gateway/restart (require_admin).
- Status-Tab: Steuerung (Backend-Dropdown + Modell, Wechseln/Neustart) mit
  Poll bis das Gateway wieder antwortet; Hinweis auf systemd-Voraussetzung.
- Tests: Auth + Allowlist (Shell-Metazeichen/unbekanntes Modell -> 422). 165 grün.
- Doku §7.5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:39:59 +02:00
Dieter Schlüter
97ae0a5d34 feat(admin): LLM-/GPU-Status-Karte (read-only) — Plan-Schritt 2
- app/admin_llm.py: read-only Statusabfragen ohne sudo (docker ps, ollama ps,
  nvidia-smi, systemctl --user) mit sicheren Defaults bei fehlenden Tools.
- GET /api/admin/llm/status (require_admin): Backend, Modell, Backend-Status,
  geladene Ollama-Modelle, GPU-Auslastung, Gateway-Dienst-Status.
- Admin Status-Tab: LLM-Backend-Karte mit GPU-Balken.
- Tests: Auth-Gate + Antwortschema (160 grün).
- Doku §7.5: Status-Tab um LLM/GPU-Karte ergänzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:33:06 +02:00
Dieter Schlüter
bd20a308de feat(tts): Emojis/Markdown beim Vorlesen entfernen + Autostart/GPU-Doku
- spoken_response_adapter: Emoji-/Symbol-Strip (Emoticons, Symbole, Flaggen,
  Pfeile, Dingbats, ZWJ/Variationsselektoren). Markdown wurde bereits entfernt.
- Geräte-TTS (app.js v28) liest das bereinigte "spoken"-Feld statt des Rohtexts
  (automatisch + beim Replay); Anzeige bleibt Originaltext.
- deploy/ollama-keepalive.conf: Drop-in OLLAMA_KEEP_ALIVE=5m -> GPU im Leerlauf frei.
- Doku §4.10: Dauerbetrieb als Dienst + warum der Gateway die GPU nicht blockiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:25:45 +02:00
Dieter Schlüter
317ad2ff97 feat(llm): Make-Targets zum Wechseln des LLM-Backends (Ollama <-> llama.cpp)
- scripts/llm-server/switch-llm.sh: gibt GPU des anderen Backends frei
  (llama.cpp-Container stoppen bzw. Ollama-Modelle entladen, Dienst bleibt),
  startet das gewünschte Backend, passt LOCAL_LLM_* in .env an und startet
  das Gateway (als Dienst) neu bzw. weist auf manuellen Neustart hin.
- Makefile: Targets `llm-ollama` / `llm-llamacpp` (Modell via OLLAMA_MODEL=...).
- Doku §4.7 + Schnellbefehle §4.0: Make-Targets als empfohlener Weg, inkl.
  Erklärung warum der Gateway-Neustart nötig ist (Makefile exportiert .env als
  Env-Variablen -> Vorrang vor .env-Datei).

Getestet: Round-Trip ollama -> llamacpp -> ollama; llama.cpp 2,3s, gemma3 warm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 15:56:42 +02:00
Dieter Schlüter
878bf785dd feat: Geräte-TTS, native Stimmen, Favicon, Auth-Gate, UI-Fixes
Web-UI / TTS:
- Geräte-TTS ("📱 Gerät"): Antwort wird on-device vorgelesen (Web Speech
  API), Server sendet nur Text (text_only) -> spart Bandbreite/Kosten.
  Mobil-Default, geräte-lokale Speicherung, iOS-Autoplay-Freischaltung.
- Vorlese-Symbol (🔊) je Bubble: Hybrid-Replay (Assistent-PCM gecacht,
  Eingabe via /api/speak); SVG-Icon mit kontrastreicher Farbe.
- Kombiniertes Sprachmenü (Flex + feste Sprachen) statt separatem Modus-Menü.
- "Neues Gespräch"-Button (frische Session gegen Sprach-Trägheit).
- Dark-Mode: lesbare <option>-Popups (Kontrast-Fix).
- Favicon (SVG + PNG-Fallbacks) aus mund.png.

TTS-Backend:
- Sprache wird an alle TTS-Provider durchgereicht; Piper-Stimme folgt der
  Sprache; Chatterbox mehrsprachig + cross-lingual.
- Native Referenz-Stimmen je Sprache (config/voices/<lang>.wav, FLEURS CC-BY),
  loudness-normalisiert.

LLM-Sprache:
- Antwort folgt zuverlässig der gewählten Sprache (verstärkte Anweisung +
  Erinnerung an der letzten Nutzer-Nachricht gegen History-Trägheit).

Admin / Auth:
- Wörterbuch: alle 8 Sprachen, Zeilen editierbar, alphabetische Sortierung.
- Web-UI hinter Auth-Gate (Redirect auf SSO_LOGIN_URL / 401); Favicons offen.
- Log-Tab: Hinweis, wenn der systemd-Dienst nicht aktiv ist.
- Einstellungen: Hinweis "pro Nutzer überschreibbar" bei Sprache/Modus/Qualität.

Doku (BEDIENUNGSANLEITUNG.md): Geräte-TTS §6.5.0, Fix/Flex §6.6, native
Stimmen §6.5.3, llama.cpp<->Ollama-Wechsel §4.7, Auth/SSO §7.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 13:12:04 +02:00
Dieter Schlüter
dc8ac80505 docs: § 6.5.4 Aussprache-Lexika vollständig dokumentiert (alle 8 Sprachen)
- YAML-Dateistruktur für alle Sprachen erklärt (de/en/fr/es/it/nl/ru/zh)
- Drei Sektionen (abbreviations/units/terms) mit Matching-Regeln
- Anleitung "Eigennamen in Fremdsprachen" — Textersetzungs-Prinzip statt IPA
  mit Klangäquivalent-Tabelle (ʃ, y/ü, x, ts in je 6 Sprachen)
- Sonderfall RU/ZH: Kyrillisch/Hanzi notwendig, Lateinschrift unzuverlässig
- Drei Wege dokumentiert: Web-UI (de/en), REST-API (alle Sprachen, sofort),
  direkte YAML-Bearbeitung (alle Sprachen, Neustart nötig)
- Test-Befehle: Admin-Test-Button, curl, Normalizer-Skript
- § 7.5 Wörterbuch-Tab: Hinweis auf de/en-Beschränkung + Verweis auf § 6.5.4
- API-Referenz: lang-Parameter auf alle Sprachcodes erweitert
- Stichwortverzeichnis: zwei neue Einträge

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:34:08 +02:00
Dieter Schlüter
0074593002 feat(tts): Aussprache-Lexika für alle 8 Piper-Sprachen — Eigenname Dieter Schlüter
Neue Dateien: pronunciation.fr/es/it/nl/ru/zh.yaml
Erweitert:    pronunciation.en.yaml

Strategie: Textersetzung in der Zielsprach-Orthographie (kein IPA nötig).
- EN: "Deeter Schlueter" (ie→/aɪ/ umgehen, ü als ue)
- FR: "Chluteur" (ch=/ʃ/, u=/y/=ü, eur=/œʁ/ → /ʃlytœʁ/)
- NL: "Schluuter" (uu=/yː/=ü, sch=/sx/)
- ES/IT: "Schlueter" (kein /ʃ/ verfügbar, beste Näherung)
- RU: Kyrillisch "Дитер Шлютер" (espeak-ru liest Latein schlecht)
- ZH: Hanzi "迪特 施吕特" (施=/ʃɨ/, 吕=lǚ=/ly/=ü — trifft Klang genau)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:29:22 +02:00
Dieter Schlüter
81d4cf6fd8 fix(llm): LLM antwortet jetzt in der konfigurierten Systemsprache
Bisher wurde `language` zwar an STT/TTS übergeben, aber nie an den LLM.
Lokales Modell antwortete deshalb immer auf Englisch/Deutsch, egal ob
Französisch, Spanisch o.a. eingestellt war.

Lösung: `language`-Parameter durch die gesamte LLM-Schicht gezogen:
- base.py: `lang_instruction()` helper + Signatur erweitert
- local_openai_compatible.py: Sprachanweisung ("Respond in Français.")
  wird als letzter System-Part in den Message-Stack eingefügt
- openrouter.py: Explizite Sprachanweisung ergänzt den bestehenden
  "Answer in the same language as the user"-Prompt
- fallback.py: FallbackLLMProvider leitet `language` durch
- orchestrator.py: alle 3 LLM-Aufrufstellen übergeben `language`
- Tests: alle Stub-LLMs um `language=None` ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:12:16 +02:00
Dieter Schlüter
c3f6206d0c feat(piper): beste Stimmen für 10 Sprachen installiert + low/medium aufgeräumt (v19)
Neu installiert: en_US-lessac-high, en_GB-cori-high, es_ES-sharvard-medium,
ru_RU-irina-medium, zh_CN-huayan-medium, it_IT-paola-medium, nl_NL-mls-medium.
Gelöscht: de_DE-kerstin-low, de_DE-karlsson-low, fr_FR-gilles-low,
es_ES-davefx-medium. Alle verbleibenden Stimmen haben 130–159 Phoneme;
es/fr/it/nl/ru/zh haben kein high — medium ist das Maximum bei piper.
Dropdown und Doku aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:41:03 +02:00
Dieter Schlüter
389cd5a417 fix: fr_FR-siwis-medium installiert — Missing-phoneme-Warning behoben (v18)
fr_FR-gilles-low (low, 130 Phoneme) fehlt Kombinations-Tilde U+0303
→ Nasalvokale (ã, ɔ̃) werden nicht korrekt ausgesprochen.
fr_FR-siwis-medium (medium, 154 Phoneme) enthält alle nötigen Phoneme.
Dropdown und Doku aktualisiert. espeak-ng-Daten waren bereits vollständig
in piper gebündelt — kein separater Download nötig.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:23:42 +02:00
Dieter Schlüter
da561cec09 fix: de_DE-karlsson-low installiert — Dropdown + Doku aktualisiert (v17)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:07:24 +02:00
Dieter Schlüter
b91be9b9fe fix: Piper-Stimmliste korrigieren — karlsson-low nicht installiert (v16)
app.js: de_DE-karlsson-low aus Dropdown entfernt (nicht vorhanden);
stattdessen alle tatsächlich installierten Stimmen aufgeführt
(de_DE-thorsten-high, de_DE-kerstin-low, en_US-ryan-high,
es_ES-davefx-medium, fr_FR-gilles-low).
BEDIENUNGSANLEITUNG § 6.5.2: Tabelle auf tatsächlichen Bestand korrigiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:04:49 +02:00
Dieter Schlüter
a54daecbc8 feat: make stop/start/restart + Doku servicefreundlicher
Makefile: neue Targets 'stop' (Gateway + llama.cpp), 'start' (llm-up + run),
'restart' (stop + start). CONTAINER_NAME als überschreibbare Variable.
BEDIENUNGSANLEITUNG § 4: neuer Schnellbefehle-Block (§ 4.0) mit Tabellen für
alle Start-/Stop-Situationen; § 4.8 Stoppen und § 4.9 Neustart als dedizierte
Abschnitte. README: Stoppen, Neu starten und make-Targets aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:58:54 +02:00
Dieter Schlüter
11ae66f5ae fix(admin): TTS-Test-Button nutzt jetzt richtigen Provider (v15)
piper_voice, openrouter_tts_model und openrouter_tts_voice bekommen
'testProvider' in FIELD_META. Der Test-Handler schickt diesen Provider
als tts_provider im Request mit — statt immer den System-Default zu
nutzen, der oft openrouter ist und dann mit 502 scheitert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:39:14 +02:00
Dieter Schlüter
4a4cbe643d feat(admin): Provider & Sprache — Eigener-Wert-Eingabe + Tests (v14)
app.js: default_stt/llm/tts_provider und default_language von 'select'
auf 'combo' umgestellt, damit neben den vordefinierten Optionen jederzeit
ein beliebiger eigener Wert eingegeben werden kann (Zukunftssicherheit).
combo-Renderer unterstützt jetzt auch meta.labels (Sprachkürzel → Klarname).
tests/test_admin_settings.py: 28 Tests für GET/PUT/DELETE /api/admin/config —
Auth, bekannte Werte, Custom-Values, Persistenz, Runtime-Wirkung.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:30:52 +02:00
Dieter Schlüter
a892896d32 docs: Backend-Wechsel dokumentieren + Konzept-Datei Mobile TTS hinzufügen
README + BEDIENUNGSANLEITUNG § 4.7: vollständige Kommandos für den Wechsel
zwischen llama.cpp und Ollama (inkl. Stopp-Befehle für den jeweils anderen
Backend-Prozess). TOC und Sachregister aktualisiert.
Docs/Neues_Konzept_mit_TTS_auf_Mobil_Geraet.md: neue Recherche-/Konzept-Datei
zu On-Device-TTS auf iOS/Android hinzugefügt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:12:29 +02:00
Dieter Schlüter
d9cb5ffc7e docs: Startup-Kommandos vollständig dokumentieren (llama.cpp, Ollama, Gateway)
README: neuer Abschnitt „Starten — alle Szenarien" mit allen Optionen für
cloud/hybrid/local-dev, llama.cpp-ENV-Tabelle, Ollama-Sequenz und make-Targets.
BEDIENUNGSANLEITUNG: § 4.5 (llama.cpp) um direkten Script-Aufruf, docker logs,
vollständige ENV-Tabelle erweitert; neues § 4.6 (Ollama) mit systemctl/serve,
pull/list/ps, .env-Snippet und Reasoning-Hinweis. TOC und Sachregister aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:59:04 +02:00
Dieter Schlüter
d8b861db26 feat(web): Einstellungen-Tab – typisierte Inputs, Gruppen, Test-Buttons (v13)
- Dropdown-Auswahl für Provider, Bool- und Enum-Felder
- Combo-Widget (Dropdown + Textfeld, synchronisiert) für Modelle/Stimmen
- Range-Slider für Temperature, Number-Input für Token/Limit-Felder
- Textarea für System-Prompt
- Test-Buttons: 🔊 TTS via WebAudio, 💬 LLM via /api/chat?debug=true, 🎤 STT-Verfügbarkeitscheck
- Inline-Save-Feedback (grün ✓ / rot ✗), kein Full-Reload mehr
- ENV-Variablenname in UPPERCASE angezeigt
- Sieben Gruppen-Sektionen statt flacher Liste
- Piper-Stimmentabelle in Doku korrigiert (nur installierte: thorsten/kerstin/karlsson)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 02:35:40 +02:00
Dieter Schlüter
5d022aaf63 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>
2026-06-19 01:39:36 +02:00
Dieter Schlüter
296138ac1e fix(web): escHtml verarbeitet Zahlen korrekt, Status-Tab Fehlertext verbessert
escHtml() rief .replace() auf Number-Werten auf (httpTotal aus Metriken)
→ TypeError. Fix: immer String(str) konvertieren. Außerdem console.error
im Status-Tab-catch für einfachere Fehlerdiagnose.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 01:33:49 +02:00
Dieter Schlüter
ea923ffc61 fix(auth): require_admin akzeptiert jetzt auch SSO-Admin-Nutzer
Vorher pruefte require_admin nur den X-Admin-Key-Header — SSO-Admins
(aus ADMIN_USERS) bekamen 401. Das brach alle Admin-Panel-Tabs ausser
der Nutzerliste im Browser.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 01:24:04 +02:00
Dieter Schlüter
366d71ba26 feat(admin): Admin-Panel Phase 2 — Wörterbuch-CRUD, Live-Log, DB-Export, Metriken-Balken
- Aussprache-Lexikon (de/en) komplett im Browser editierbar: Einträge
  hinzufügen/löschen in Sektionen Abkürzungen/Einheiten/Begriffe;
  LRU-Cache wird nach jedem Schreibvorgang automatisch geleert.
- Live-Log-Tab: WebSocket auf /api/admin/log streamt journalctl
  des voice-assistant.service live im Terminal-Style ins Admin-Panel.
- DB-Export: SQLite-Datenbank über /api/admin/db-export herunterladen.
- Metriken-Tab: CSS-Balkendiagramm (Anfragen je Nutzer) ergänzt.
- Dokumentation: §7.5 Admin-Web-Panel, B.5 API-Endpunkte, Sachregister.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 01:15:28 +02:00
Dieter Schlüter
b38992cf28 feat(admin): vollständiges Admin-Panel in der Web-UI (Phase 1)
Ersetzt das rudimentäre Nutzer-Dropdown durch ein vollwertiges
Admin-Panel-Overlay mit 5 Tabs:

Nutzer:      Anlegen (Token einmalig), Umbenennen, Token-Reset,
             Löschen, Erinnerungen verwalten (anzeigen/add/delete)
Gespräche:   Nutzer → Sessions → Transkript (Chat-Bubble-Ansicht)
Notfälle:    Tabelle aller emergency_events mit Zeitstempel/Kategorie
Status:      Gateway-Status, Provider-Config, Laufzeit-Metriken
Metriken:    Nutzungsstatistik pro Nutzer + Gesamtsumme

Backend: 7 neue Admin-Endpunkte in admin.py, 5 neue Store-Methoden.
Design: Tailwind CSS, Tag/Nacht-Modus, responsiv.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 01:04:20 +02:00
Dieter Schlüter
8e48ab6d66 fix(terminal): Barge-in funktioniert jetzt in beiden Phasen
Barge-in im Sprech-Loop (voice_loop.py) war nur während der LLM-Phase
zuverlässig. Während der Audio-Wiedergabe (nach done-Event) war der
_stdin_watcher bereits gecancelt, sodass Enter ignoriert wurde oder
als Spurious-Input in record_utterance() landete.

Lösung:
- _stdin_watcher läuft jetzt durch beide Phasen (LLM + Audio)
- Phase 2: _close_player wird als asyncio-Task gegen den Watcher geract;
  Enter während der Wiedergabe killt den Player sofort
- termios.tcflush nach jedem Turn verhindert Rest-Enters im stdin-Buffer

Außerdem: OPENROUTER_API_KEY in .env ergänzt (systemd sourct kein
.bashrc); Dokumentation entsprechend aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:39:00 +02:00
Dieter Schlüter
613b18055b fix(barge-in): interrupted-Event schliesst WS -> busy-Reset + Button zurueck auf gruen
Server sendet nach Interrupt {'type':'interrupted'}, aber der Client-Handler
fehlte: WS blieb offen, runTurn loeste nie auf, Button blieb dauerhaft gelb.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:03:03 +02:00
Dieter Schlüter
6d63b8d063 fix(stt): CUDA-OOM vermeiden + Fehlermeldung bei leerer Transkription verbessern
- .env: FASTER_WHISPER_DEVICE=cpu gesetzt; llama.cpp belegt GPU 1 komplett,
  faster-whisper (small-Modell) laeuft problemlos auf CPU (~1-2s Latenz)
- ws.py: "empty transcript" -> "Keine Sprache erkannt — bitte erneut sprechen."

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:48:36 +02:00
Dieter Schlüter
6693c0411c docs(barge-in): Bedienungsanleitung um Barge-in-Nutzerbedienung ergaenzen
- §5.1.2 Tabelle: Mic-Button mit allen 3 Zustaenden (gruen/rot/amber) beschrieben
- §5.1.4: Barge-in-Schritt im Sprachaufnahme-Ablauf erklaert
- §5.2: [Enter] waehrend Wiedergabe = Barge-in im Terminal-Ablauf eingetragen
- §6.8: Barge-in jetzt fuer alle drei Kanaele beschrieben (Web, Terminal, API)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:44:53 +02:00
Dieter Schlüter
0d907a1886 feat(barge-in): Unterbrechen der KI-Antwort per Mikrofon-Button und Enter
Web-Interface (app.js):
- Mic-Button hat jetzt 3 Zustände: idle (grün 🎤) / Aufnahme (rot pulsierend) /
  KI antwortet (amber ⏹, klicken = Barge-in)
- Barge-in sendet {"type":"interrupt"} auf der aktiven WS-Verbindung (activeWs)
  und stoppt alle laufenden AudioBufferSourceNodes sofort (stopAudio)
- busy + Button-Reset erfolgen automatisch wenn die WS schliesst

Terminal (voice_loop.py):
- _stdin_watcher(): polling via select.select (nicht-blockierend, kein Thread-Leak)
  wartet auf Enter-Tastendruck waehrend Server antwortet
- _send_and_play() raced one_turn gegen _stdin_watcher mit asyncio.wait;
  bei Barge-in: interrupt-Frame an Server, Player.terminate(), feeder beenden
- Feedback: "↩ Unterbrochen — drücke [Enter] für neue Aufnahme …"

Server-Seite war bereits vollstaendig implementiert (ws.py, kein Handlungsbedarf).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:38:33 +02:00
Dieter Schlüter
68b02f42a1 feat(users): SSO-Nutzer-Personalisierung + Admin-Endpunkte
Hintergrund: SSO-Nutzer (va.linix.de) werden bereits beim ersten Besuch
automatisch registriert, hatten aber keinen echten Namen im LLM-Kontext
und konnten vom Admin nicht vorbereitet werden.

Änderungen:
- ws.py + chat.py: Nutzeridentität (display_name + Erinnerungen) wird als
  führende System-Message bei jeder Anfrage injiziert; für anonyme
  Dev-Nutzer (AUTH_ENABLED=false) wird diese Injection übersprungen
- store.py: update_display_name() im ABC und SQLiteStore
- schemas.py: UserUpdate (display_name)
- admin.py:
  - PUT /api/admin/users/{id}: Anzeigenamen eines SSO-Nutzers setzen
  - POST /api/admin/users/{id}/memories: initiale Erinnerungen vorbelegen
- BEDIENUNGSANLEITUNG §7.2: neuer Abschnitt "SSO-Nutzer — automatische
  Registrierung" mit vollständigem Workflow; §7.3/7.4 neu nummeriert;
  Anhang B.5 mit neuen Endpunkten ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:55:05 +02:00
Dieter Schlüter
b76ec76c45 feat(admin): Token-Reset-Endpunkt + Doku-Klarstellung zu Token-Verwaltung
- POST /api/admin/users/{user_id}/token: neues Bearer-Token ausstellen
  (alter Token sofort ungültig, Nutzerdaten bleiben erhalten)
- Store ABC + SQLiteStore: reset_token() implementiert
- BEDIENUNGSANLEITUNG §7.2: erklärt warum Tokens nicht abrufbar sind (nur
  SHA256-Hash gespeichert), wo ADMIN_API_KEY nachzuschauen ist (.env),
  wie Token-Reset genutzt wird; praktisches Tipp zu ~/.bashrc
- Anhang B.5: neuen Endpunkt in REST-Referenz eingetragen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:36:41 +02:00
Dieter Schlüter
8ef982f297 docs: TOKEN-Herkunft in § 8 erklaeren 2026-06-18 17:29:04 +02:00
Dieter Schlüter
4970bdc85a feat(admin): DELETE /api/admin/users/{id} — Nutzer und alle Daten löschen
- Store.delete_user() löscht User + Sessions + Nachrichten + Erinnerungen + Nutzung
  (atomic, anonymer Nutzer geschützt)
- DELETE /api/admin/users/{user_id} (Admin-Key erforderlich)
  → 200 {"deleted":"..."} | 404 | 400 (anonymous)
- BEDIENUNGSANLEITUNG: $URL-Erklärung am Anfang, § 7.2 vollständig mit
  Anlegen/Anzeigen/Löschen-Beispielen inkl. realer Beispiel-Antworten

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:23:25 +02:00
Dieter Schlüter
5b9b9bbdcf docs: Dokumentation vollständig überarbeitet und neu strukturiert
README.md auf kompakte Landing Page reduziert (~84 Zeilen): Kurzbeschreibung,
Features, 30-Sekunden-Quickstart, Dokumentenübersicht mit Zielgruppen-Einstiegspunkten,
Projektstruktur.

BEDIENUNGSANLEITUNG.md von Grund auf neu geschrieben (~1530 Zeilen):
- Klickbares Inhaltsverzeichnis (14 Abschnitte + 4 Anhänge)
- Zielgruppen-Labels je Abschnitt (👤 Endnutzer / 🔧 Admin / 💻 Entwickler)
- Konsolidierte Profilbeschreibungen (cloud/hybrid/local-dev) mit Hardware,
  Software, Kosten, Latenz, Qualität, Einrichtungsbefehlen — alles an einer Stelle
- Vollständige Querverweise zwischen Abschnitten
- Anhang A: alle Umgebungsvariablen als vollständige Referenz
- Anhang B: alle API-Endpunkte (REST + WebSocket) mit Event-Typen
- Anhang C: Provider-Übersicht
- Anhang D: Sachregister mit >40 Einträgen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 16:55:05 +02:00
Dieter Schlüter
5e6d708038 docs: Betriebsprofile, Ollama, Web-UI und Startup-Hinweise dokumentieren
- README: Ollama als LLM-Backend (Alternative zu llama.cpp) mit .env-Snippet
- README: Warnung dass Gateway ohne LLM-Server startet, Fehler erst beim ersten Request
- BEDIENUNGSANLEITUNG: Abschnitt 4 zu vollstaendiger Profilbeschreibung ausgebaut
  (cloud / hybrid / local-dev je mit Hardware, Software, Kosten, Latenz, Qualitaet,
  Einrichtungsbefehlen und Vergleichstabelle)
- BEDIENUNGSANLEITUNG: neues Kapitel A0 Web-Interface (Oberflaeche, Ablaeufe,
  Fehlertabelle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 16:30:46 +02:00
Dieter Schlüter
fc75b3b07c docs: Lizenz auf proprietaer (alle Rechte vorbehalten) umstellen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 11:03:15 +02:00
Dieter Schlüter
9ec9fd4732 docs: MIT-Lizenz (LICENSE.md) hinzufuegen + README-Verweis
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:59:59 +02:00
Dieter Schlüter
d87ebdf457 fix(web): robuster Mikrofon-Pfad fuer iOS-Safari + mp4-Format
- getUserMedia/MediaRecorder-Support pruefen, klare Fehlermeldung mit error.name
  (In-App-Browser/Privat-Modus/Permission) statt pauschalem "HTTPS noetig?"
- Safari liefert audio/mp4 (nicht webm): tatsaechlichen mimeType verwenden und als
  format mitschicken; Server (faster-whisper) erkennt das Format am Inhalt (mp4/AAC
  verifiziert). Asset-Version v7

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:39:02 +02:00
Dieter Schlüter
7ebba4e755 feat(web): Tailwind-Redesign-UI + Doku (Nachtrag zu 3470c0e)
Eigentliche UI-Dateien: neues Tailwind-Layout (index.html), Theme-Umschalter +
Bubble-Stile + Mic-Status (app.js); README-Abschnitt aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:20:24 +02:00
Dieter Schlüter
3470c0e262 feat(web): Tailwind-Redesign, Tag/Nacht-Umschalter, Mobile-Fixes
- responsives, modernes Layout mit Tailwind (CDN, kein Build); aufgeraeumter Header
  (einheitliche Groessen/Farben, neutrale Palette, ein Akzent)
- Tag-/Nacht-Umschalter: manuell + folgt automatisch dem System (kein Flackern)
- Mobile: App-Shell h-[100dvh] mit fixem Eingabe-Footer + safe-area-Padding
  -> kein grosser Abstand mehr zum unteren Bildschirmrand
- "Metriken"-Link entfernt (fuer Endnutzer irrelevant); Admin-Nutzerliste als Toggle
- style.css entfernt (komplett Tailwind), Asset-Version v6

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:19:34 +02:00
Dieter Schlüter
56114309f5 docs(deploy): Chatterbox-TTS-Dienst (GPU-Pinning per UUID, no_playback) ergaenzen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 09:58:15 +02:00
Dieter Schlüter
b7c59d8c85 feat(web): Stimmen-Umschalter (piper/chatterbox/cloud) in der UI
Dropdown im Menue setzt tts_provider als Request-Override fuer Text- und Sprach-Turns
-> live umschaltbar zwischen schnellem piper, hochwertigem chatterbox und Cloud.
Asset-Version v5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 09:49:25 +02:00
Dieter Schlüter
5f01607b79 feat(tts): Chatterbox-Provider (hohe Qualitaet + Voice-Cloning) anbinden
Loest #7. Der chatterbox-Stub wird durch eine echte Anbindung an den lokalen
Chatterbox-HTTP-Dienst ersetzt (POST /speak -> /status pollen -> GET /audio, WAV).
no_playback=true -> der Dienst spielt nicht lokal ab, liefert nur Bytes. WAV->PCM
(24 kHz, Resampling bei Bedarf). Waehlbar via tts_provider=chatterbox; piper bleibt
der schnelle Default (chatterbox ist ~echtzeit-langsam, dafuer klonbare Stimme).

- config: CHATTERBOX_BASE_URL/_VOICE/_LANG/_SPEED/_TIMEOUT; Registry verdrahtet
- Tests: gemockter httpx (Synthese, WAV/Resample, Job-Fehler, Stimmenwahl)
- Doku: README, Architektur (#7), deploy/README (GPU-Pinning per UUID, no_playback)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 09:44:31 +02:00