Fix TTS time phrases and STT cleanup

This commit is contained in:
2026-03-15 16:22:00 +03:00
parent cb54a9ee75
commit 6add70fcd2
4 changed files with 146 additions and 2 deletions

View File

@@ -234,6 +234,33 @@ class SpeechRecognizer:
return True
def _run_blocking_cleanup_sync(self, func, timeout_seconds: float, label: str) -> bool:
"""Sync-версия _run_blocking_cleanup() для use-case в listen()."""
done_event = threading.Event()
error_holder = {}
def runner():
try:
func()
except Exception as exc:
error_holder["error"] = exc
finally:
done_event.set()
thread = threading.Thread(target=runner, daemon=True, name=label)
thread.start()
done_event.wait(timeout=max(0.0, float(timeout_seconds)))
if not done_event.is_set():
print(f"⚠️ {label} timed out; continuing cleanup.")
return False
error = error_holder.get("error")
if error is not None:
print(f"⚠️ {label} failed: {error}")
return False
return True
async def _process_audio(
self, dg_connection, timeout_seconds, detection_timeout, fast_stop
):
@@ -334,6 +361,7 @@ class SpeechRecognizer:
# --- Задача отправки аудио с буферизацией ---
sender_stop_event = threading.Event()
stream_holder = {"stream": None}
def request_stop():
stop_event.set()
@@ -346,6 +374,7 @@ class SpeechRecognizer:
try:
stream, stream_sample_rate = self._open_stream_for_session()
stream_holder["stream"] = stream
options = LiveOptions(
model="nova-2", # Самая быстрая и точная модель
language=self.current_lang,
@@ -465,6 +494,7 @@ class SpeechRecognizer:
with contextlib.suppress(Exception):
if stream:
stream.close()
stream_holder["stream"] = None
print(f"\n🛑 Stream stopped. Chunks sent: {chunks_sent}")
sender_thread = threading.Thread(
@@ -544,23 +574,56 @@ class SpeechRecognizer:
sender_thread,
timeout_seconds=max(SENDER_STOP_WAIT_SECONDS, SENDER_FORCE_RELEASE_WAIT_SECONDS),
)
cleanup_unhealthy = False
if not sender_stopped:
print("⚠️ Audio sender shutdown timed out; continuing cleanup.")
cleanup_unhealthy = True
def force_close_stream():
stream = stream_holder.get("stream")
if not stream:
return
with contextlib.suppress(Exception):
if stream.is_active():
stream.stop_stream()
with contextlib.suppress(Exception):
stream.close()
stream_holder["stream"] = None
await self._run_blocking_cleanup(
force_close_stream,
timeout_seconds=SENDER_FORCE_RELEASE_WAIT_SECONDS,
label="STT audio stream force close",
)
# Дадим шанс потоку выйти после принудительного закрытия.
sender_stopped = await self._wait_for_thread(sender_thread, timeout_seconds=0.6)
if not sender_stopped:
cleanup_unhealthy = True
# Небольшая пауза, чтобы получить последние transcript-события перед finish().
await asyncio.sleep(DEEPGRAM_FINALIZATION_GRACE_SECONDS)
# Завершаем соединение и ждем последние результаты
await self._run_blocking_cleanup(
finish_ok = await self._run_blocking_cleanup(
dg_connection.finish,
timeout_seconds=DEEPGRAM_FINISH_TIMEOUT_SECONDS,
label="Deepgram finish",
)
if not finish_ok:
cleanup_unhealthy = True
final_text = self.transcript.strip()
if not final_text:
final_text = latest_interim.strip()
self.transcript = final_text
if cleanup_unhealthy:
# Если текст уже получен, не теряем команду пользователя.
# Но сбрасываем клиента, чтобы следующая STT-сессия стартовала на чистом соединении.
self.dg_client = None
if final_text:
return final_text
raise RuntimeError("Deepgram session cleanup timed out")
return final_text
def listen(
@@ -622,10 +685,20 @@ class SpeechRecognizer:
# Закрываем соединение, если оно было создано
if dg_connection:
try:
dg_connection.finish()
self._run_blocking_cleanup_sync(
dg_connection.finish,
timeout_seconds=DEEPGRAM_FINISH_TIMEOUT_SECONDS,
label="Deepgram finish (error cleanup)",
)
except:
pass # Игнорируем ошибки при завершении
# Принудительно сбрасываем клиента, чтобы след. попытка не унаследовала
# подвисшее соединение SDK.
self.dg_client = None
with contextlib.suppress(Exception):
self.initialize()
if attempt < 2: # Не ждем после последней попытки
print(f"⚠️ Не удалось подключиться к Deepgram, попытка {attempt + 1}/3, повторяю...")
time.sleep(1) # Уменьшаем задержку между попытками