first commit
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
"""Тест Модуля 4: brain (LLM) + очистка Markdown + полная цепочка.
|
||||
|
||||
Запуск (нужен ключ: файл .env со строкой OPENROUTER_API_KEY=sk-or-v1-...):
|
||||
python modules/brain/test_brain.py clean # тест очистки без сети
|
||||
python modules/brain/test_brain.py --ask "Привет!" # вопрос → ответ (raw + чистый + время)
|
||||
python modules/brain/test_brain.py --ask "Привет!" --play # и озвучить ответ
|
||||
python modules/brain/test_brain.py --model google/gemini-flash-1.5 --ask "..."
|
||||
python modules/brain/test_brain.py --mic # ПОЛНАЯ ЦЕПЬ: говоришь → слышишь ответ
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from modules.brain import clean_for_speech, get_provider
|
||||
except ImportError:
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
from modules.brain import clean_for_speech, get_provider
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
def clean_selftest() -> None:
|
||||
"""Проверка очистки Markdown без сети: грязные строки → чистый текст."""
|
||||
cases = [
|
||||
("**Привет!** Как *дела*?", "Привет! Как дела?"),
|
||||
("## Заголовок\n- пункт один\n- пункт два", "Заголовок пункт один пункт два"),
|
||||
("1. Включи компьютер\n2. Открой программу", "Включи компьютер Открой программу"),
|
||||
("Ссылка: [Google](https://google.com)", "Ссылка: Google"),
|
||||
("Код: `python -m venv` и ```print(1)```", "Код: python -m venv и"),
|
||||
("Смайл 😊 и стрелка ➡️ тут", "Смайл и стрелка тут"),
|
||||
("Текст выше\n> Важная цитата\nТекст ниже", "Текст выше Важная цитата Текст ниже"),
|
||||
("Раздел один\n---\nРаздел два", "Раздел один Раздел два"),
|
||||
]
|
||||
ok = True
|
||||
for dirty, expected in cases:
|
||||
got = clean_for_speech(dirty)
|
||||
status = "OK " if got == expected else "FAIL"
|
||||
ok &= got == expected
|
||||
print(f"[{status}] {dirty!r}\n → {got!r}" + ("" if got == expected else f"\n ожидалось {expected!r}"))
|
||||
print("\nОЧИСТКА:", "ПРОЙДЕНА ✅" if ok else "ЕСТЬ ОШИБКИ ❌")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
def ask_once(brain, question: str, play: bool, history=None):
|
||||
result = brain.ask(question, history=history)
|
||||
print(f"\nВопрос: {question}")
|
||||
print(f"Ответ ({result.generation_sec:.2f} c): {result.cleaned_text}")
|
||||
if result.raw_text != result.cleaned_text:
|
||||
print(f"(raw был: {result.raw_text!r})")
|
||||
if play:
|
||||
from modules.audio_io import Player
|
||||
from modules.tts import get_provider as get_tts
|
||||
|
||||
tts = get_tts("edge")
|
||||
speech = tts.synthesize(result.cleaned_text)
|
||||
print(f"TTS: синтез {speech.generation_sec:.2f} c, аудио {speech.duration_sec:.1f} c")
|
||||
Player().play(speech.audio, speech.samplerate, blocking=True)
|
||||
return result
|
||||
|
||||
|
||||
def mic_loop(brain) -> None:
|
||||
"""Полная цепочка: запись с микрофона → STT → LLM → TTS → воспроизведение."""
|
||||
from modules.audio_io import Player, Recorder
|
||||
from modules.stt import get_provider as get_stt
|
||||
from modules.tts import get_provider as get_tts
|
||||
|
||||
print("Загружаю STT (faster-whisper small)...")
|
||||
stt = get_stt("faster-whisper", model_size="small")
|
||||
tts = get_tts("edge")
|
||||
player = Player()
|
||||
rec = Recorder()
|
||||
history = []
|
||||
|
||||
print("\nГОТОВ. [Enter] → говори вопрос → [Enter] → услышишь ответ. Пустой Enter — выход.")
|
||||
while True:
|
||||
cmd = input("> ").strip()
|
||||
if cmd == "":
|
||||
break
|
||||
|
||||
rec.start()
|
||||
input(" 🔴 запись... Enter = закончить ")
|
||||
audio = rec.stop()
|
||||
if len(audio) == 0:
|
||||
print(" Слишком коротко — повтори.")
|
||||
continue
|
||||
|
||||
stt_res = stt.transcribe(audio)
|
||||
question = stt_res.text.strip()
|
||||
if not question:
|
||||
print(" STT ничего не расслышал. Повтори.")
|
||||
continue
|
||||
print(f" Ты: {question}")
|
||||
|
||||
llm = brain.ask(question, history=history)
|
||||
print(f" AI ({llm.generation_sec:.2f} c): {llm.cleaned_text}")
|
||||
|
||||
speech = tts.synthesize(llm.cleaned_text)
|
||||
print(f" TTS ({speech.generation_sec:.2f} c), играю...")
|
||||
player.play(speech.audio, speech.samplerate, blocking=True)
|
||||
|
||||
history.append({"role": "user", "text": question})
|
||||
history.append({"role": "assistant", "text": llm.cleaned_text})
|
||||
history = history[-8:] # держим контекст, но ограничиваем
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="Тест Модуля 4: brain")
|
||||
p.add_argument("mode", nargs="?", default="ask", choices=["ask", "clean", "mic"],
|
||||
help="clean = офлайн-тест очистки; ask = текстовый вопрос; mic = полная цепь")
|
||||
p.add_argument("--ask", "--question", dest="question", default="Привет! Кто ты и что умеешь?",
|
||||
help="вопрос к модели (режим ask)")
|
||||
p.add_argument("--model", default="google/gemma-3-27b-it",
|
||||
help="модель OpenRouter (deepseek/deepseek-chat-v3-0324, qwen/qwen-2.5-72b-instruct...)")
|
||||
p.add_argument("--play", action="store_true", help="озвучить ответ (edge-tts)")
|
||||
p.add_argument("--turns", type=int, default=0,
|
||||
help="сколько вопросов подряд в режиме ask с общей историей")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
|
||||
if args.mode == "clean":
|
||||
clean_selftest()
|
||||
return 0
|
||||
|
||||
brain = get_provider("openrouter", model=args.model)
|
||||
print(f"Модель: {args.model}")
|
||||
|
||||
if args.mode == "mic":
|
||||
mic_loop(brain)
|
||||
return 0
|
||||
|
||||
questions = [args.question]
|
||||
if args.turns > 1:
|
||||
extra = input(f"Введи ещё {args.turns - 1} вопроса через ';' : ")
|
||||
questions = [q.strip() for q in extra.split(";") if q.strip()] or questions
|
||||
|
||||
history = []
|
||||
for q in questions:
|
||||
result = ask_once(brain, q, play=args.play, history=history)
|
||||
history += [{"role": "user", "text": q}, {"role": "assistant", "text": result.cleaned_text}]
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user