feat: Enhance local command handling and introduce local intents
- Refactor `run_local_command` to manage subprocesses more effectively, ensuring child processes are terminated on timeout. - Introduce `_terminate` function to handle process group termination and capture output. - Implement `_command_output` to format command results with a character limit. - Add local intent recognition in `intents.py` to handle commands like "stop", "go to sleep", and "come here" without server interaction. - Normalize user input to match local intents while stripping filler words. - Update tests to cover new local intent functionality and ensure proper command handling. - Enhance speech processing to handle abbreviations and improve spoken output clarity.
This commit is contained in:
@@ -112,3 +112,51 @@ def test_pcm_to_wav_bytes_round_trips_via_wave_module():
|
||||
assert wf.getframerate() == 16000
|
||||
frames = wf.readframes(wf.getnframes())
|
||||
assert np.frombuffer(frames, dtype=np.int16).tolist() == pcm.tolist()
|
||||
|
||||
|
||||
# ── flushing buffered audio ─────────────────────────────────────────────────
|
||||
|
||||
class _BufferedStream:
|
||||
"""A stream with a backlog, like PortAudio's ring buffer after the reader
|
||||
was blocked on a network call for a while."""
|
||||
|
||||
def __init__(self, available):
|
||||
self.read_available = available
|
||||
self.reads = []
|
||||
|
||||
def read(self, frames):
|
||||
self.reads.append(frames)
|
||||
self.read_available = max(0, self.read_available - frames)
|
||||
return np.zeros((frames, 1), dtype=np.int16), False
|
||||
|
||||
|
||||
def test_flush_drops_exactly_what_was_buffered():
|
||||
stream = _BufferedStream(4096)
|
||||
assert mic.flush(stream) == 4096
|
||||
assert stream.reads == [4096]
|
||||
assert stream.read_available == 0
|
||||
|
||||
|
||||
def test_flush_is_bounded_so_it_cannot_chase_a_live_stream():
|
||||
"""A stream filling as fast as it drains must not spin forever."""
|
||||
stream = _BufferedStream(10 ** 9)
|
||||
dropped = mic.flush(stream, max_seconds=1.0, sample_rate=16000)
|
||||
assert dropped == 16000
|
||||
|
||||
|
||||
def test_flush_is_a_noop_on_an_empty_or_fake_stream():
|
||||
stream = _BufferedStream(0)
|
||||
assert mic.flush(stream) == 0
|
||||
assert stream.reads == []
|
||||
assert mic.flush(_ScriptedStream([])) == 0 # no read_available at all
|
||||
assert mic.flush(None) == 0
|
||||
|
||||
|
||||
def test_flush_swallows_a_device_error():
|
||||
class _Broken:
|
||||
read_available = 1024
|
||||
|
||||
def read(self, frames):
|
||||
raise RuntimeError("device disappeared")
|
||||
|
||||
assert mic.flush(_Broken()) == 0
|
||||
|
||||
Reference in New Issue
Block a user