128 lines
5.2 KiB
Python
128 lines
5.2 KiB
Python
"""Тест М5.3: свободный режим с ПРОБЕЛОМ-тумблером диалога.
|
||
|
||
Запуск:
|
||
python modules/assistant/test_free.py
|
||
python modules/assistant/test_free.py --silence 1.5 # короче пауза конца фразы
|
||
|
||
Схема (целевая для отца):
|
||
ПРОБЕЛ (в консоли: d + Enter) — НАЧАТЬ диалог: куи «Ассистент готов к диалогу»
|
||
→ просто говори, пауза 2 c = фраза закончена → ответ.
|
||
ПРОБЕЛ в диалоге — ЗАВЕРШИТЬ: куи «Спасибо. До новых встреч».
|
||
Или скажи «до свидания» — то же самое + запомнит разговор.
|
||
Вне диалога микрофон заглушен. Выход: q + Enter.
|
||
"""
|
||
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 HandsFreeRecorder, Player
|
||
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 HandsFreeRecorder, Player
|
||
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
|
||
|
||
VAD_STATE_RU = {
|
||
"listening": "👂 слушаю тебя",
|
||
"speech_start": "🎙 начало фразы",
|
||
"speech_end": "✅ фраза собрана, обрабатываю",
|
||
"too_short": "⚠ слишком коротко — пропускаю",
|
||
"paused": "⏸ не слушаю (сама говорю)",
|
||
}
|
||
|
||
ASSISTANT_STATE_RU = {
|
||
"idle": "⏸ ожидание",
|
||
"listening": "🎙 запись",
|
||
"thinking": "🧠 думаю…",
|
||
"speaking": "🔊 говорю",
|
||
}
|
||
|
||
|
||
def main() -> int:
|
||
p = argparse.ArgumentParser(description="Свободный режим ассистента (VAD)")
|
||
p.add_argument("--silence", type=float, default=2.0,
|
||
help="пауза тишины, считающаяся концом фразы (сек)")
|
||
p.add_argument("--threshold", type=float, default=0.5,
|
||
help="порог VAD: больше — строже, меньше — чувствительнее")
|
||
p.add_argument("--min-speech", type=float, default=0.3,
|
||
help="минимальная доля голоса во фразе, сек")
|
||
p.add_argument("--stt-model", default="small")
|
||
args = p.parse_args()
|
||
|
||
print("Загружаю модули…")
|
||
stt = get_stt("faster-whisper", model_size=args.stt_model)
|
||
brain = get_brain("openrouter")
|
||
tts = make_tts() # провайдер/скорость из .env (TTS_PROVIDER, TTS_RATE)
|
||
player = Player()
|
||
|
||
quit_event = threading.Event()
|
||
|
||
def on_vad_state(state: str) -> None:
|
||
print(f"\n [микрофон] {VAD_STATE_RU.get(state, state)}")
|
||
|
||
def on_assistant_state(state: str, note: str) -> None:
|
||
print(f"\n[{ASSISTANT_STATE_RU.get(state, state)}]" + (f" {note}" if note else ""))
|
||
|
||
def on_log(msg: str) -> None:
|
||
print(f" {msg}")
|
||
|
||
recorder = HandsFreeRecorder(
|
||
silence_sec=args.silence,
|
||
speech_threshold=args.threshold,
|
||
min_speech_sec=args.min_speech,
|
||
on_state=on_vad_state,
|
||
on_error=lambda m: print(f" [ОШИБКА] {m}"),
|
||
)
|
||
assistant = Assistant(stt, brain, tts, recorder, player,
|
||
on_state_change=on_assistant_state, on_log=on_log)
|
||
|
||
print(f"\nУПРАВЛЕНИЕ (голосом, без клавиатуры):")
|
||
print(f" «{assistant.wake_word.wake_word.capitalize()}» — начать диалог (куи «готова к диалогу»)")
|
||
print(" «До свидания» — завершить диалог (куи «до новых встреч»)")
|
||
print(" Вне диалога ассистент слушает тихо и отвечает только на wake-word.")
|
||
print(" Консоль: s — «Замолчи», r — «Повтори», ПРОБЕЛ — тоже тумблер диалога, q — выход.\n")
|
||
|
||
quit_event = threading.Event()
|
||
|
||
events = KeyEvents(
|
||
on_talk_down=lambda: None,
|
||
on_talk_up=lambda: None,
|
||
on_stop=assistant.on_stop,
|
||
on_repeat=assistant.on_repeat,
|
||
on_quit=lambda: quit_event.set(),
|
||
on_dialog_toggle=assistant.on_dialog_toggle,
|
||
)
|
||
backend = get_backend("console", events)
|
||
backend.start()
|
||
recorder.start() # микрофон открыт, но заглушен до начала диалога
|
||
|
||
try:
|
||
while not quit_event.wait(timeout=0.2):
|
||
pass
|
||
except KeyboardInterrupt:
|
||
print("\n(Ctrl+C)")
|
||
finally:
|
||
backend.stop()
|
||
recorder.stop()
|
||
try:
|
||
player.stop()
|
||
except Exception:
|
||
pass
|
||
print("Выход.")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main()) |