Files
cosmo-voice-assistant/cosmo/transcriber.py
d.klimov 6010816f1d Initial commit: Cosmo voice assistant
Полностью локальный голосовой ассистент на Python.

Стек:
- Wake word: openWakeWord (onnxruntime)
- STT: RealtimeSTT + faster-whisper + Silero VAD (CUDA)
- LLM-агент: smolagents ToolCallingAgent + Ollama qwen2.5:7b
- TTS: Silero V4 (torch.hub) + sounddevice
- Shell: Git Bash (Windows) / bash (macOS)

Поддерживает Windows и macOS. Агент с памятью и tool calling —
находит программы самостоятельно, запоминает пути, выполняет
произвольные shell-команды.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 15:58:12 +03:00

88 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
STT модуль на базе RealtimeSTT.
Использует faster-whisper + Silero VAD под капотом.
Поддерживает стриминг — partial transcriptions во время речи.
"""
import threading
from RealtimeSTT import AudioToTextRecorder
from loguru import logger
class Transcriber:
def __init__(self, config: dict):
whisper_cfg = config["whisper"]
audio_cfg = config["audio"]
self._recorder: AudioToTextRecorder | None = None
self._config = {
"model": whisper_cfg["model_size"],
"language": whisper_cfg["language"],
"device": whisper_cfg["device"],
"compute_type": whisper_cfg["compute_type"],
# Silero VAD параметры
"silero_sensitivity": 0.4,
"webrtc_sensitivity": 3,
"post_speech_silence_duration": audio_cfg["silence_duration"],
"min_length_of_recording": 0.3,
"min_gap_between_recordings": 0.01,
# Отключаем wake word в RealtimeSTT — используем свой
"wakeword_backend": "none",
# Не запускать в режиме непрерывного прослушивания
"use_microphone": True,
"spinner": False,
"level": 0, # минимальный лог уровень внутри RealtimeSTT
}
logger.info(
f"Инициализирую RealtimeSTT: модель={whisper_cfg['model_size']}, "
f"device={whisper_cfg['device']}, compute={whisper_cfg['compute_type']}"
)
self._init_recorder()
def _init_recorder(self):
try:
self._recorder = AudioToTextRecorder(**self._config)
logger.info("RealtimeSTT готов")
except Exception as e:
logger.error(f"Ошибка инициализации RealtimeSTT: {e}")
raise
def record_and_transcribe(self, on_partial: callable = None) -> str:
"""
Записывает команду и транскрибирует.
on_partial(text) — опциональный колбэк для частичных результатов.
Возвращает финальный текст.
"""
if self._recorder is None:
self._init_recorder()
result_holder = []
done_event = threading.Event()
def on_text(text: str):
result_holder.append(text)
done_event.set()
# Partial results — показываем что слышим в реальном времени
if on_partial:
self._recorder.on_realtime_transcription_update = on_partial
logger.info("Слушаю команду...")
self._recorder.text(on_text)
done_event.wait(timeout=12.0)
text = result_holder[0].strip() if result_holder else ""
if text:
logger.info(f"Транскрипция: '{text}'")
else:
logger.info("Команда не распознана (тишина или таймаут)")
return text
def shutdown(self):
if self._recorder:
try:
self._recorder.shutdown()
except Exception:
pass