first commit

This commit is contained in:
workD12
2026-09-11 19:47:15 +03:00
commit 32754a1fe2
56 changed files with 3836 additions and 0 deletions
+128
View File
@@ -0,0 +1,128 @@
"""Тест М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())