Files
2026-09-11 19:47:15 +03:00

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())