feat(voice): Wake-Word per Sprachbefehl ausschalten

"Computer, Wake-Word aus" / "mach das Ohr aus" / "hör auf zuzuhören" / "geh
schlafen" → die App stoppt den Wake-Word-Listener KOMPLETT (Mikro frei, echte
Ruhe). Wieder-An nur über den Ohr-Button (bewusst, weil dann taub — Voice-
Wieder-An ist physikalisch unmöglich, wenn nichts mehr hört).

Deterministischer Detektor _user_wants_wake_off im Brain (kein LLM-Call nötig,
still). Signal fließt als wake_off durch ChatOut → Bridge → App-Payload; die App
löst denselben Pfad wie toggleWakeWord-off aus (cancelRecording +
wakeWordService.stop() + setWakeWordActive(false)). Detektor gegen 13 Positiv-
+ 9 Negativ-Fällen verifiziert (fängt nicht 'Licht aus' / 'Konversation Ende').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 20:27:48 +02:00
co-authored by Claude Opus 4.8
parent c4e658446d
commit 93401d42d1
4 changed files with 66 additions and 2 deletions
+4
View File
@@ -676,6 +676,9 @@ class ChatOut(BaseModel):
# Task fertig ist)? Dann pausiert die App die Projekt-Queue und leitet die
# naechste Eingabe als Antwort weiter, statt sie als neuen Auftrag anzustellen.
awaiting_reply: bool = False
# Der User hat per Sprache "Wake-Word aus" gesagt → die App stoppt den
# Wake-Word-Listener komplett (Mikro frei). Wieder-An nur ueber App-Button.
wake_off: bool = False
# Echo der project_id die dieser Turn hatte. Bridge nutzt sie damit die
# ausgehende Chat-Bubble sauber getaggt in der richtigen Thread-Bahn der
# UI landet.
@@ -785,6 +788,7 @@ async def chat(body: ChatIn, background: BackgroundTasks):
speak=speak,
converse=converse,
awaiting_reply=awaiting_reply,
wake_off=(answered_by == "wake-off"),
)
finally:
_project_pending[pid] = [