51 lines
2.1 KiB
Python
51 lines
2.1 KiB
Python
"""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, ""
|