"""Тест Модуля 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())