108 lines
4.4 KiB
Python
108 lines
4.4 KiB
Python
"""Тест Модуля 5: горячие клавиши + машина состояний = ЖИВОЙ ассистент.
|
|
|
|
Запуск:
|
|
python modules/assistant/test_assistant.py # console-бэкенд (Linux, без root)
|
|
python modules/assistant/test_assistant.py --backend keys # глобальные клавиши (Windows; Linux — root)
|
|
|
|
Консольный режим (имитация трёх клавиш):
|
|
Enter — нажать «Слушай» (первый раз), отпустить (второй раз) — аналог удержания
|
|
s + Enter — «Замолчи»
|
|
r + Enter — «Повтори»
|
|
q + Enter — выход
|
|
|
|
Критерии приёмки Модуля 5:
|
|
1. Пустой вопрос: Enter → молчание → Enter → «Слишком коротко».
|
|
2. Обычный цикл: Enter → вопрос → Enter → ответ голосом (диалог помнит контекст).
|
|
3. «Замолчи» во время речи — звук стихает мгновенно (цель < 100 мс).
|
|
4. «Повтори» — последняя фраза звучит снова.
|
|
5. Нажать «Слушай» ВО ВРЕМЯ речи — речь стихла, запись началась.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
import threading
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from modules.assistant import Assistant
|
|
from modules.audio_io import Player, Recorder
|
|
from modules.brain import get_provider as get_brain
|
|
from modules.hotkeys import KeyEvents, get_backend
|
|
from modules.stt import get_provider as get_stt
|
|
from modules.tts import make_tts
|
|
except ImportError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
|
from modules.assistant import Assistant
|
|
from modules.audio_io import Player, Recorder
|
|
from modules.brain import get_provider as get_brain
|
|
from modules.hotkeys import KeyEvents, get_backend
|
|
from modules.stt import get_provider as get_stt
|
|
from modules.tts import make_tts
|
|
|
|
STATES_RU = {
|
|
"idle": "⏸ ожидание",
|
|
"listening": "🎙 слушаю",
|
|
"thinking": "🧠 думаю…",
|
|
"speaking": "🔊 говорю",
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Живой ассистент (Модуль 5)")
|
|
parser.add_argument("--backend", default="console", choices=["console", "keys"])
|
|
parser.add_argument("--stt-model", default="small")
|
|
args = parser.parse_args()
|
|
|
|
print("Загружаю модули…")
|
|
stt = get_stt("faster-whisper", model_size=args.stt_model)
|
|
brain = get_brain("openrouter") # модель из .env
|
|
tts = make_tts() # провайдер/скорость из .env (TTS_PROVIDER, TTS_RATE)
|
|
recorder = Recorder()
|
|
player = Player()
|
|
|
|
def on_state(state: str, note: str) -> None:
|
|
line = STATES_RU.get(state, state)
|
|
print(f"\n[{line}]" + (f" {note}" if note else ""))
|
|
|
|
assistant = Assistant(stt, brain, tts, recorder, player, on_state_change=on_state)
|
|
|
|
quit_event = threading.Event()
|
|
|
|
events = KeyEvents(
|
|
on_talk_down=assistant.on_talk_down,
|
|
on_talk_up=assistant.on_talk_up,
|
|
on_stop=assistant.on_stop,
|
|
on_repeat=assistant.on_repeat,
|
|
on_quit=lambda: quit_event.set(),
|
|
)
|
|
|
|
if args.backend == "keys":
|
|
print("\nГлобальные клавиши: «Слушай» = space (удерживать), "
|
|
"«Замолчи» = esc, «Повтори» = enter. Ctrl+C — выход.")
|
|
print("(На Linux этот режим требует root: sudo -E .venv/bin/python …)")
|
|
else:
|
|
print("\nКонсольные клавиши (имитация удержания):")
|
|
print(" Enter — нажать «Слушай», ещё Enter — отпустить (начать обработку)")
|
|
print(" s + Enter — «Замолчи» | r + Enter — «Повтори» | q + Enter — выход")
|
|
|
|
backend = get_backend(args.backend, events)
|
|
backend.start()
|
|
|
|
try:
|
|
while not quit_event.wait(timeout=0.2):
|
|
pass
|
|
except KeyboardInterrupt:
|
|
print("\n(Ctrl+C)")
|
|
finally:
|
|
backend.stop()
|
|
try:
|
|
player.stop() # тишина при выходе
|
|
except Exception:
|
|
pass
|
|
print("Выход.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |