Bolt Assistant: wake-phrase voice assistant for Bolt
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"""Finding the wake phrase in a transcript, and what was said after it.
|
||||
|
||||
>>> find_wake("Hey Bolt, what's the weather?", WAKE)
|
||||
(True, "what's the weather?")
|
||||
>>> find_wake("tighten that bolt", WAKE)
|
||||
(False, '')
|
||||
|
||||
Rules, all aimed at not waking up on ordinary conversation:
|
||||
|
||||
- The phrase has to **open** what was said. Only filler may come before it
|
||||
("um, hey Bolt", "okay so Bolt"), at most two words of it, so "tighten
|
||||
that bolt" and "the bolt on the door" never count.
|
||||
- Whole words only: "boltage" is not "bolt".
|
||||
- Longest phrase wins, so "hey thunderbolt" isn't read as "hey" + garbage.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Iterable, Tuple
|
||||
|
||||
_WORD = re.compile(r"[a-z0-9']+")
|
||||
_LEAD_SLACK = 2 # filler words allowed before the wake phrase
|
||||
_FILLER = {"um", "uh", "erm", "er", "so", "oh", "ok", "okay", "well", "and", "right",
|
||||
"alright", "yeah", "hmm", "hm", "ah", "hi", "hey", "yo"}
|
||||
|
||||
|
||||
def _words(text: str) -> list:
|
||||
"""Lower-case words with their character spans in the original text."""
|
||||
return [(m.group(0), m.start(), m.end()) for m in _WORD.finditer(text.lower())]
|
||||
|
||||
|
||||
def find_wake(transcript: str, phrases: Iterable[str]) -> Tuple[bool, str]:
|
||||
"""(heard, remainder): whether a wake phrase opens *transcript*, and the
|
||||
request that followed it in the same breath ("" if it was just the
|
||||
phrase)."""
|
||||
words = _words(transcript or "")
|
||||
if not words:
|
||||
return False, ""
|
||||
candidates = sorted({tuple(_WORD.findall(p.lower())) for p in phrases if p.strip()},
|
||||
key=len, reverse=True)
|
||||
for start in range(min(_LEAD_SLACK + 1, len(words))):
|
||||
if any(word not in _FILLER for word, _, _ in words[:start]):
|
||||
break # a real word came first: this isn't addressed to Bolt
|
||||
for phrase in candidates:
|
||||
span = words[start:start + len(phrase)]
|
||||
if len(span) == len(phrase) and all(w == p for (w, _, _), p in zip(span, phrase)):
|
||||
rest = transcript[span[-1][2]:]
|
||||
return True, rest.lstrip(" \t,.;:!?-—").strip()
|
||||
return False, ""
|
||||
Reference in New Issue
Block a user