diff --git a/BEDIENUNGSANLEITUNG.md b/BEDIENUNGSANLEITUNG.md
index 99dc59c..4c7630b 100644
--- a/BEDIENUNGSANLEITUNG.md
+++ b/BEDIENUNGSANLEITUNG.md
@@ -774,29 +774,43 @@ https://va.beispiel.de/ ← remote über Reverse-Proxy (alles, inkl. Mikr
```
┌─────────────────────────────────────────────────────────┐
-│ Voice Assistant [🇩🇪 ▾] [Qualität ▾] [⚙️] [🌙] │
-├─────────────────────────────────────────────────────────┤
-│ │
-│ Nachrichtenverlauf │
-│ (eigene Nachrichten: blaue Blase rechts) │
-│ (Assistent: graue Blase links) │
-│ │
-├─────────────────────────────┬───────────────────────────┤
-│ Texteingabe … [Senden] │ [🎤] │
-└─────────────────────────────┴───────────────────────────┘
- Statuszeile: „denkt …" / „verarbeite Sprache …" / leer
+│ 👄 Voice Assistant ............... [🇩🇪 ▾] [ ⋮ ] │
+├─────────────────────────────────────────────────────────┤ ┌─ Menü (⋮) ──────────────┐
+│ │ │ VORLESEN │
+│ Nachrichtenverlauf │ │ ▣ 📱 Im Gerät │
+│ (eigene Nachrichten: blaue Blase rechts) │ │ ▢ ☁ Server │
+│ (Assistent: graue Blase links) │ │ ▢ ✨ Beste Qualität │
+│ │ │ ────────────────────── │
+├─────────────────────────────┬───────────────────────────┤ │ ✎ Neues Gespräch │
+│ Texteingabe … [Senden] │ [🎤] │ │ 🌙 Tag-/Nachtmodus │
+└─────────────────────────────┴───────────────────────────┘ │ ────────────────────── │
+ Statuszeile: „denkt …" / „verarbeite Sprache …" / leer │ Angemeldet · Admin · Ab │
+ └──────────────────────────┘
```
+**Kopfzeile (immer schlank):**
+
| Element | Funktion |
|---------|----------|
+| **👄 / Titel** | Marke; Mund-Symbol als Wiedererkennung |
+| **Sprache ▾** | Antwortsprache (→ § 6.6): `🔄 Flex` = folgt der gesprochenen Sprache · feste Sprache (🇩🇪/🇬🇧/…) = Antwort + Stimme in dieser Sprache |
+| **⋮ Menü** | Öffnet die Einstellungen (unten) |
+
+**Im ⋮-Menü:**
+
+| Element | Funktion |
+|---------|----------|
+| **Vorlesen** | Wie wird die Antwort vorgelesen? `📱 Im Gerät` = das Handy liest selbst vor (kein Server-Audio → spart Daten) · `☁ Server` = piper (lokal, zuverlässig) · `✨ Beste Qualität` = chatterbox (natürliche Stimme). „Im Gerät" erscheint nur, wo der Browser es unterstützt. |
+| **✎ Neues Gespräch** | Frische Sitzung (Verlauf zurücksetzen) |
+| **🌙 Tag-/Nachtmodus** | Heller/dunkler Modus; folgt sonst dem Betriebssystem |
+| **Konto** | „Angemeldet als …", **⚙ Admin** (nur Admins → § 7.5), **Abmelden** |
+
+**Unten:**
+
+| Element | Funktion |
+|---------|----------|
+| **🎤 Mikrofon** | **grün:** tippen → Aufnahme · **rot:** tippen → stoppt & sendet · **amber ⏹:** tippen → KI unterbrechen (Barge-in). STT läuft serverseitig (Whisper). |
| **Texteingabe + Senden** | Text tippen, dann Enter oder „Senden" |
-| **🎤 Mikrofon-Button** | **Idle (grün 🎤):** Tippen → Aufnahme startet · **Aufnahme (rot pulsierend 🎤):** Tippen → Aufnahme stoppt und wird gesendet · **KI antwortet (amber ⏹):** Tippen → Antwort sofort unterbrechen (Barge-in) |
-| **Sprache ▾** | Antwortsprache wählen (→ § 6.6): `🔄 Flex` = folgt automatisch der gesprochenen Sprache · feste Sprache (🇩🇪/🇬🇧/…) = Eingabe wird in diese Sprache übersetzt. Die vorlesende Stimme folgt der Auswahl. |
-| **Qualität ▾** (Ausgabe/TTS) | Vorlese-Quelle: `📱 Gerät` = das Handy/der Browser liest selbst vor (Web Speech API, kein Server-Audio → spart Daten/Kosten) · `Schnell` = piper (lokal) · `Hoch` = chatterbox (neuronal) · `Cloud` = openrouter |
-| **STT ▾** (Eingabe) | *Nur sichtbar auf Geräten mit lokaler Spracherkennung* (iOS/Android): `🎙 Gerät` = Spracherkennung im Gerät (nur Text wird gesendet) · `☁ Server` = Audio-Upload + Whisper (wie am PC/Laptop). Getrennt von der Ausgabe wählbar (→ § 6.3.1). |
-| **☀️ / 🌙** | Tag-/Nacht-Modus; folgt sonst automatisch dem Betriebssystem |
-| **⚙️** (Admin) | Öffnet das Admin-Panel — nur für Admin-Nutzer sichtbar (→ § 7.5) |
-| **Angemeldet als …** | SSO-Identität; „Gast" wenn AUTH deaktiviert oder kein SSO-Cookie |
#### 5.1.3 Typischer Ablauf — Textchat
@@ -1013,34 +1027,10 @@ FASTER_WHISPER_DEVICE=cuda
FASTER_WHISPER_COMPUTE_TYPE=float16
```
-#### 6.3.1 Geräte-STT (Web Speech API) — „🎙 Gerät"
-
-Auf **Mobilgeräten** (iOS/Android) kann die Spracherkennung **im Gerät** laufen: Das
-Handy wandelt Sprache in Text um und sendet nur den **Text** an den Server — kein
-Audio-Upload, keine Server-STT-Last. Gegenstück zum Geräte-TTS (§ 6.5.0), aber
-**getrennt** wählbar (STT-Dropdown in der Kopfzeile).
-
-- **Erscheint nur**, wenn das Gerät lokale Spracherkennung bietet. **Terminal, Desktop-
- Browser und Laptop** zeigen die Option nicht — dort läuft STT wie bisher über den
- Server (Whisper).
-- **Datenschutz (Linie C):** Standardmäßig nur bei **nachweislich lokaler** Erkennung
- (iOS-Safari; Chrome mit On-Device-Modus). **Chrome am Desktop** würde das Audio zur
- Erkennung an Google schicken — das ist nur erlaubt, wenn ein Admin
- `ALLOW_CLOUD_STT=true` setzt (Admin → Einstellungen → „Geräte-STT (mobil)").
-- **Nur Fix-Sprache:** Geräte-STT braucht einen Sprach-Hint → es nutzt die im
- Sprach-Dropdown gewählte feste Sprache. Im **Flex-Modus** fällt es automatisch auf
- Server-STT (Whisper-Auto-Erkennung) zurück.
-- **On-Device-Sprachpakete (Chrome):** Vor der lokalen Erkennung wird per
- `SpeechRecognition.available()` geprüft, ob das Sprachpaket vorhanden ist. Fehlt es,
- wird es im Hintergrund heruntergeladen (`install()`) und für **diesen** Turn auf
- Cloud (falls erlaubt) bzw. Server zurückgefallen — so erscheint kein
- „language not supported" mehr.
-- **Fallback:** Ohne Unterstützung (z. B. Firefox), bei fehlendem Sprachpaket oder
- Erkennungsfehlern (`language-not-supported`, `network` …) wird automatisch der
- Server-STT-Pfad genutzt. Während der Erkennung erscheint der Text **live** im
- Eingabefeld; erneutes Tippen aufs Mikrofon beendet und sendet.
-- **Schnellwahl „📴 Lokal":** Setzt mit einem Tipp **STT + TTS** auf „Gerät" — dann
- sendet/empfängt das Handy nur noch Text (maximale Daten-/Kostenersparnis).
+> **Hinweis:** Die Spracherkennung läuft auf **allen** Geräten serverseitig (Whisper) —
+> einheitlich und zuverlässig. (Ein früher erprobtes Geräte-STT über die Web Speech API
+> wurde wieder entfernt: Auf Android-Chrome lief es nur über die Google-Cloud, und die
+> Erkennungsqualität war Whisper unterlegen. Das **Vorlesen** im Gerät (§ 6.5.0) bleibt.)
---
diff --git a/app/api/me.py b/app/api/me.py
index de0c35a..f270820 100644
--- a/app/api/me.py
+++ b/app/api/me.py
@@ -3,7 +3,6 @@ from fastapi import APIRouter, Depends, HTTPException
from app.auth import require_user, is_admin_user
from app.config import settings
from app.dependencies import get_store
-from app.runtime_config import runtime_settings
from app.schemas import UserPrefs, MemoryCreate, MemoryOut
from app.store import User
@@ -19,7 +18,6 @@ async def get_me(user: User = Depends(require_user)):
"is_admin": user.is_admin or is_admin_user(user),
"prefs": user.prefs,
"sso_logout_url": settings.sso_logout_url,
- "allow_cloud_stt": bool(runtime_settings.allow_cloud_stt),
}
diff --git a/app/config.py b/app/config.py
index 56397f7..14ce23f 100644
--- a/app/config.py
+++ b/app/config.py
@@ -135,10 +135,6 @@ class Settings(BaseSettings):
faster_whisper_model: str = "base" # tiny|base|small|medium|large-v3
faster_whisper_device: str = "auto" # auto|cpu|cuda
faster_whisper_compute_type: str = "default" # default|int8|float16|int8_float16
- # Geräte-STT (Web Speech API) nur bei nachweislich lokaler Erkennung (iOS, Chrome
- # on-device). True erlaubt es auch bei Cloud-Erkennung (z. B. Chrome-Desktop ->
- # Audio geht zu Google). Default False = strenger Datenschutz (Linie C).
- allow_cloud_stt: bool = False
# --- Lokales TTS (piper) -------------------------------------------------
piper_bin: str = "piper" # Pfad/Name des piper-Binaries
piper_voices_dir: str = str(Path.home() / ".local" / "share" / "piper" / "voices")
diff --git a/app/runtime_config.py b/app/runtime_config.py
index cb809bc..231e413 100644
--- a/app/runtime_config.py
+++ b/app/runtime_config.py
@@ -34,7 +34,6 @@ RUNTIME_SETTABLE: dict[str, tuple[str, str, str]] = {
"memory_extraction_enabled": ("Erinnerungs-Extraktion", "bool", "true | false"),
"memory_extraction_every_n_turns": ("Extraktion alle N Turns", "int", "z.B. 3"),
"daily_request_limit": ("Tageskontingent (global)", "int", "0 = unbegrenzt"),
- "allow_cloud_stt": ("Geräte-STT via Cloud erlauben", "bool", "true = auch Cloud-Erkennung (z.B. Chrome-Desktop -> Audio zu Google); false = nur on-device"),
}
_TTL = 30.0
diff --git a/app/web/app.js b/app/web/app.js
index 6ad9a9f..999fa3d 100644
--- a/app/web/app.js
+++ b/app/web/app.js
@@ -9,8 +9,6 @@ const formEl = $("#prompt-form");
const micBtn = $("#mic");
const ttsSel = $("#tts");
const langSel = $("#lang-sel");
-const sttSel = $("#stt-sel");
-const allLocalBtn = $("#all-local");
// "Flex" ist im Sprachmenü nur ein weiterer Wert: keine feste Sprache, sondern
// automatische Erkennung. Eine konkrete Sprache = Fix-Modus (Eingabe wird übersetzt).
@@ -105,153 +103,18 @@ function unlockTTS() {
try { speechSynthesis.speak(new SpeechSynthesisUtterance("")); _ttsUnlocked = true; } catch (e) {}
}
-// ---------- Geräte-STT (Web Speech API SpeechRecognition) ----------
-// Gegenstück zum Geräte-TTS: das Gerät erkennt Sprache lokal und schickt nur Text.
-// Linie C: standardmäßig nur bei nachweislich lokaler Erkennung (iOS, Chrome on-device);
-// Cloud-Erkennung (z. B. Chrome-Desktop -> Audio zu Google) nur, wenn der Admin
-// allow_cloud_stt aktiviert hat. Sonst greift weiter der Server-STT (Whisper).
-const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
-const STT_SUPPORTED = !!SR;
-const STT_KEY = "va-stt"; // geräte-lokal: "device" wenn Geräte-STT gewählt
-let meAllowCloudStt = false; // aus /api/me (Admin-Flag)
-
-function isIOS() {
- const ua = navigator.userAgent || "";
- return /iPhone|iPad|iPod/i.test(ua) ||
- (navigator.maxTouchPoints > 1 && /Macintosh/.test(ua));
-}
-// Heuristik: iOS erkennt on-device. Chrome mit processLocally-Unterstützung gilt
-// ebenfalls als lokal. Sonst (Cloud) nur erlaubt, wenn der Admin es freigibt.
-function sttIsLocal() {
- if (isIOS()) return true;
- try { return SR && "processLocally" in SR.prototype; } catch (e) { return false; }
-}
-function sttDeviceAllowed() {
- return STT_SUPPORTED && (sttIsLocal() || meAllowCloudStt);
-}
-function isSttDeviceMode() { return !!sttSel && sttSel.value === "device"; }
-
-// Geräte-STT für DIESEN Turn nutzen? Nur Fix-Sprache (SpeechRecognition braucht einen
-// Sprach-Hint); im Flex-Modus auf Server-STT (Whisper-Auto-Erkennung) zurückfallen.
-function useDeviceStt() {
- return isSttDeviceMode() && sttDeviceAllowed() && (langSel && langSel.value !== "flex");
-}
-
-// Stellt das STT-Dropdown beim Laden ein: Option ausblenden wenn nicht erlaubt;
-// sonst lokale Wahl bzw. Mobil-Default.
-function applySttChoice() {
- if (!sttSel) return;
- if (!sttDeviceAllowed()) {
- sttSel.classList.add("hidden"); // kein Geräte-STT -> Server wie bisher
- sttSel.value = "server";
- return;
- }
- sttSel.classList.remove("hidden");
- const local = localStorage.getItem(STT_KEY);
- if (local === "device") sttSel.value = "device";
- else if (local === "server") sttSel.value = "server";
- else if (isMobile()) { sttSel.value = "device"; localStorage.setItem(STT_KEY, "device"); }
- // Schnellwahl "Alles lokal" nur zeigen, wenn STT- UND TTS-on-device möglich sind.
- if (allLocalBtn && TTS_SUPPORTED) allLocalBtn.classList.remove("hidden");
-}
-
-// Schnellwahl: STT + TTS auf "Gerät" (nur Text zum Server). Setzt beide Schalter.
-function setAllLocal() {
- unlockTTS();
- if (sttSel) { sttSel.value = "device"; localStorage.setItem(STT_KEY, "device"); }
- if (ttsSel) { ttsSel.value = "device"; localStorage.setItem(PLAYBACK_KEY, "device"); }
- statusEl.textContent = "Alles lokal: Erkennung + Vorlesen im Gerät";
- setTimeout(() => {
- if (statusEl.textContent.startsWith("Alles lokal")) statusEl.textContent = "";
- }, 2500);
-}
-
-let recognition = null;
-
-// Bestimmt den besten STT-Modus für eine Sprache: "local" (on-device), "cloud" oder
-// "server" (Whisper-Fallback). Nutzt die Chrome-API available()/install() für die
-// On-Device-Sprachpakete — verhindert "language not supported", wenn das Paket fehlt.
-async function resolveSttMode(bcp) {
- // Chrome (neue API): prüft, ob das On-Device-Sprachpaket vorhanden ist.
- if (SR && typeof SR.available === "function") {
- try {
- const a = await SR.available({ langs: [bcp], processLocally: true });
- if (a === "available") return "local";
- if (a === "downloadable" || a === "downloading") {
- // On-Device-Paket anstoßen (für künftige Turns); jetzt Cloud/Server.
- try { if (typeof SR.install === "function") SR.install({ langs: [bcp], processLocally: true }); } catch (e) {}
- }
- return meAllowCloudStt ? "cloud" : "server";
- } catch (e) { /* API-Fehler -> unten */ }
- }
- // Keine available()-API: iOS-Safari erkennt on-device (mit Live-Vorschau) -> lokal.
- if (isIOS()) return "local";
- // Sonst (z. B. älteres Chrome): SpeechRecognition liefe über die Cloud.
- return meAllowCloudStt ? "cloud" : "server";
-}
-
-const SERVER_STT_MSG = "Server-Erkennung. Live-Vorschau nur mit lokalem/Cloud-STT";
-
-async function startDeviceStt() {
- const bcp = LANG_BCP47[langSel.value] || "de-DE";
- const mode = await resolveSttMode(bcp);
- if (mode === "server") { // lokal (für diese Sprache) nicht da & Cloud verboten
- startRecording(SERVER_STT_MSG);
- return;
- }
- const rec = new SR();
- rec.lang = bcp;
- rec.interimResults = true;
- rec.continuous = false;
- if (mode === "local") { try { rec.processLocally = true; } catch (e) {} }
- recognition = rec;
- let finalText = "";
- setMicRecording();
- statusEl.textContent = "höre zu … (zum Stoppen erneut tippen)";
- rec.onresult = (e) => {
- let interim = "";
- for (let i = e.resultIndex; i < e.results.length; i++) {
- const t = e.results[i][0].transcript;
- if (e.results[i].isFinal) finalText += t; else interim += t;
- }
- promptEl.value = (finalText + interim).trim(); // Live-Feedback im Eingabefeld
- };
- rec.onerror = (e) => {
- recognition = null;
- const err = e.error || "?";
- if (err === "no-speech") { setMicIdle(); statusEl.textContent = "nichts gehört — bitte erneut"; return; }
- // Sprache/Dienst/Netz nicht verfügbar -> sauber auf Server-STT zurückfallen statt Fehler.
- if (err === "language-not-supported" || err === "service-not-allowed" || err === "network") {
- startRecording(SERVER_STT_MSG);
- return;
- }
- setMicIdle();
- statusEl.textContent = "Spracherkennung fehlgeschlagen (" + err + ")";
- };
- rec.onend = () => {
- recognition = null;
- setMicIdle();
- const text = (promptEl.value || "").trim();
- promptEl.value = "";
- if (text) sendText(text); // -> vorhandener Text-Turn (inkl. Geräte-/Server-TTS)
- };
- try { rec.start(); } catch (e) {
- recognition = null;
- startRecording(SERVER_STT_MSG); // Start scheiterte -> Server-Fallback
- }
-}
-function stopDeviceStt() { if (recognition) { try { recognition.stop(); } catch (e) {} } }
-
const PLAYBACK_KEY = "va-playback"; // geräte-lokal: "device" wenn Geräte-TTS gewählt
-// Setzt das Qualität-Dropdown beim Laden. Vorrang:
-// 1. lokale Wahl (localStorage), 2. Server-Pref (tts_provider), 3. Mobil-Default = Gerät.
+// Setzt das verborgene Quell-Select (#tts) beim Laden + spiegelt es in die Presets.
+// Vorrang: 1. lokale Wahl (localStorage), 2. Server-Pref (tts_provider), 3. Mobil-Default = Gerät.
function applyPlaybackChoice(serverProvider) {
if (!ttsSel) return;
- // "Gerät"-Option ausblenden, wenn der Browser keine Web Speech API hat.
+ // "Im Gerät"-Preset ausblenden, wenn der Browser keine Web Speech API hat.
if (!TTS_SUPPORTED) {
const opt = ttsSel.querySelector('option[value="device"]');
if (opt) opt.remove();
+ const btn = document.querySelector('.tts-preset[data-tts="device"]');
+ if (btn) btn.classList.add("hidden");
}
const local = localStorage.getItem(PLAYBACK_KEY);
if (local === "device" && TTS_SUPPORTED) {
@@ -261,7 +124,34 @@ function applyPlaybackChoice(serverProvider) {
} else if (TTS_SUPPORTED && isMobile() && local === null) {
ttsSel.value = "device"; // Mobil-Default, solange nichts gewählt wurde
localStorage.setItem(PLAYBACK_KEY, "device");
+ } else if (!ttsSel.value) {
+ ttsSel.value = "piper"; // Fallback: Server
}
+ syncPresetUI();
+}
+
+// Markiert das aktive Ton-Preset (Häkchen + Rahmen) passend zu #tts.
+function syncPresetUI() {
+ const val = ttsSel ? ttsSel.value : "";
+ document.querySelectorAll(".tts-preset").forEach((btn) => {
+ const active = btn.dataset.tts === val ||
+ (val === "openrouter" && btn.dataset.tts === "chatterbox"); // Cloud -> "Beste Qualität"
+ btn.classList.toggle("ring-2", active);
+ btn.classList.toggle("ring-blue-500", active);
+ btn.classList.toggle("border-blue-500", active);
+ const chk = btn.querySelector(".check");
+ if (chk) chk.style.opacity = active ? "1" : "0";
+ });
+}
+
+// ---------- Einstellungs-Menü (⋮) ----------
+function openMenu() {
+ $("#settings-sheet").classList.remove("hidden");
+ $("#settings-backdrop").classList.remove("hidden");
+}
+function closeMenu() {
+ $("#settings-sheet").classList.add("hidden");
+ $("#settings-backdrop").classList.add("hidden");
}
let busy = false;
@@ -306,8 +196,6 @@ function loadMe() {
: (prefs.language || "flex");
}
applyPlaybackChoice(prefs.tts_provider);
- meAllowCloudStt = !!me.allow_cloud_stt;
- applySttChoice();
return me;
} catch (e) {
$("#identity").textContent = "Verbindung fehlgeschlagen";
@@ -666,18 +554,14 @@ micBtn.addEventListener("click", () => {
stopAudio();
statusEl.textContent = "Unterbrochen";
// busy + Button-Reset erfolgen sobald die WS schliesst (-> runTurn resolve -> sendVoice/sendText)
- } else if (recognition) {
- stopDeviceStt(); // Geräte-STT läuft -> stoppen (onend sendet den Text)
} else if (mediaRecorder && mediaRecorder.state === "recording") {
stopRecording();
- } else if (useDeviceStt()) {
- startDeviceStt(); // lokale Spracherkennung (nur Fix + erlaubt)
} else {
- startRecording(); // Server-STT (Upload) — wie bisher, auch am Desktop
+ startRecording(); // Server-STT (Upload) — überall gleich
}
});
-$("#admin-toggle").addEventListener("click", openAdminPanel);
+$("#admin-toggle").addEventListener("click", () => { closeMenu(); openAdminPanel(); });
// ---------- Neues Gespräch ----------
// Startet eine frische Session (neuer session_id) -> der bisherige Verlauf wird nicht
@@ -694,12 +578,29 @@ function newConversation() {
if (statusEl.textContent === "Neues Gespräch begonnen") statusEl.textContent = "";
}, 2500);
}
-$("#new-chat").addEventListener("click", newConversation);
+$("#new-chat").addEventListener("click", () => { closeMenu(); newConversation(); });
+
+// ---------- Menü (⋮) verdrahten ----------
+$("#menu-toggle").addEventListener("click", () =>
+ $("#settings-sheet").classList.contains("hidden") ? openMenu() : closeMenu());
+$("#settings-backdrop").addEventListener("click", closeMenu);
+document.addEventListener("keydown", (e) => { if (e.key === "Escape") closeMenu(); });
+// Ton-Presets: setzen das verborgene #tts + lösen die bestehende Persistenz-Logik aus.
+document.querySelectorAll(".tts-preset").forEach((btn) =>
+ btn.addEventListener("click", () => {
+ unlockTTS();
+ ttsSel.value = btn.dataset.tts;
+ ttsSel.dispatchEvent(new Event("change"));
+ syncPresetUI();
+ })
+);
// Tag/Nacht: Umschalter (manuell) + automatisch dem System folgen, solange nichts gewählt.
const themeBtn = $("#theme-toggle");
-const setThemeIcon = () =>
- (themeBtn.textContent = document.documentElement.classList.contains("dark") ? "☀️" : "🌙");
+const setThemeIcon = () => {
+ const icon = $("#theme-icon");
+ if (icon) icon.textContent = document.documentElement.classList.contains("dark") ? "☀️" : "🌙";
+};
themeBtn.addEventListener("click", () => {
const dark = document.documentElement.classList.toggle("dark");
localStorage.setItem("theme", dark ? "dark" : "light");
@@ -734,9 +635,6 @@ if (ttsSel) {
}
});
}
-// STT-Wahl ist geräte-lokal (nicht server-seitig) -> nur in localStorage.
-if (sttSel) sttSel.addEventListener("change", () => localStorage.setItem(STT_KEY, sttSel.value));
-if (allLocalBtn) allLocalBtn.addEventListener("click", setAllLocal);
loadMe();
@@ -1541,8 +1439,6 @@ const FIELD_META = {
labels: { "true":"Aktiviert","false":"Deaktiviert" } },
memory_extraction_every_n_turns: { ui: "number", min: 1, max: 20 },
daily_request_limit: { ui: "number", min: 0 },
- allow_cloud_stt: { ui: "select", opts: ["true","false"],
- labels: { "true":"Erlaubt (auch Cloud-Erkennung)","false":"Nur on-device (Datenschutz)" } },
};
const FIELD_GROUPS = [
@@ -1553,7 +1449,6 @@ const FIELD_GROUPS = [
{ label: "TTS-Verarbeitung", keys: ["tts_normalize_level","audio_stream_default"] },
{ label: "Gedächtnis", keys: ["memory_extraction_enabled","memory_extraction_every_n_turns"] },
{ label: "Limits", keys: ["daily_request_limit"] },
- { label: "Geräte-STT (mobil)", keys: ["allow_cloud_stt"] },
];
let _settingsData = [];
diff --git a/app/web/index.html b/app/web/index.html
index e408da1..44634b0 100644
--- a/app/web/index.html
+++ b/app/web/index.html
@@ -201,8 +201,9 @@
Voice Assistant
-
-
-
-
-
-
-
+