Two of the three quit-phrase checks in main.py still match against raw text.lower(), so a quit phrase that Whisper transcribes with an interior comma never fires. The natural, grammatically-correct way to say it ("Goodbye, Jarvis.") is exactly the case that fails.
Observed on: 84b3a6c, Python 3.12.14, macOS 26.6.2 arm64, small.en via mlx-whisper.
What happens
Say "goodbye jarvis" naturally. Whisper transcribes it as Goodbye, Jarvis. with the vocative comma. The session carries on as if nothing was said, and the reply is the agent answering the words as a normal turn. Nothing in the log indicates a quit was attempted, because none was.
It reads like a process that will not die. It was never told to quit.
Where
backtalk/main.py, on 84b3a6c:
895: elif not expired and not any(q in text.lower()
896: for q in QUIT_PHRASES):
899: if any(q in text.lower() for q in QUIT_PHRASES):
Line 899 is the main turn handler, so it is the one users actually hit.
The fix already exists in the same file. _norm_speech() at line 127 was written for precisely this, and its docstring says so: "Whisper loves interior commas ("yes, confirm"); end-stripping alone misses them." The sibling confirm check at line 891 already routes through it, and the permission-ask quit override at line 876 got the treatment too:
if _norm_speech(text) in {_norm_speech(q) for q in QUIT_PHRASES}:
Lines 895 and 899 were simply missed.
Reproduction
from backtalk.config import CFG
import backtalk.main as m
t = "Goodbye, Jarvis." # real Whisper output
print(any(q in t.lower() for q in CFG["quit_phrases"])) # False <- current
print(any(q in m._norm_speech(t) for q in CFG["quit_phrases"])) # True
Default quit_phrases from config.py:251 are all lowercase letters and spaces, so normalising the transcript alone is enough for the defaults.
Note on the workaround we are running
We patched both sites to any(q in _norm_speech(text) for q in QUIT_PHRASES) locally and it works, but that keeps substring matching, which we suspect is not what you want. The exact-match shape you already used at line 876, normalising both sides, looks like the better answer, and it also respects the reasoning in your own comment there about "No! Don't hang up, skip it" needing to stay a deny rather than kill the session. We would rather converge on your idiom than have you take ours. Happy to open a PR in whichever shape you prefer.
Related: this masked a second, more serious bug underneath it, filed as #24. Note that fixing this issue alone will make that deadlock reachable by the normal exit route for everyone, where previously only Ctrl-C hit it.
Two of the three quit-phrase checks in
main.pystill match against rawtext.lower(), so a quit phrase that Whisper transcribes with an interior comma never fires. The natural, grammatically-correct way to say it ("Goodbye, Jarvis.") is exactly the case that fails.Observed on:
84b3a6c, Python 3.12.14, macOS 26.6.2 arm64,small.envia mlx-whisper.What happens
Say "goodbye jarvis" naturally. Whisper transcribes it as
Goodbye, Jarvis.with the vocative comma. The session carries on as if nothing was said, and the reply is the agent answering the words as a normal turn. Nothing in the log indicates a quit was attempted, because none was.It reads like a process that will not die. It was never told to quit.
Where
backtalk/main.py, on84b3a6c:Line 899 is the main turn handler, so it is the one users actually hit.
The fix already exists in the same file.
_norm_speech()at line 127 was written for precisely this, and its docstring says so: "Whisper loves interior commas ("yes, confirm"); end-stripping alone misses them." The sibling confirm check at line 891 already routes through it, and the permission-ask quit override at line 876 got the treatment too:Lines 895 and 899 were simply missed.
Reproduction
Default
quit_phrasesfromconfig.py:251are all lowercase letters and spaces, so normalising the transcript alone is enough for the defaults.Note on the workaround we are running
We patched both sites to
any(q in _norm_speech(text) for q in QUIT_PHRASES)locally and it works, but that keeps substring matching, which we suspect is not what you want. The exact-match shape you already used at line 876, normalising both sides, looks like the better answer, and it also respects the reasoning in your own comment there about "No! Don't hang up, skip it" needing to stay a deny rather than kill the session. We would rather converge on your idiom than have you take ours. Happy to open a PR in whichever shape you prefer.Related: this masked a second, more serious bug underneath it, filed as #24. Note that fixing this issue alone will make that deadlock reachable by the normal exit route for everyone, where previously only Ctrl-C hit it.