Fix TTS time phrases and STT cleanup
This commit is contained in:
@@ -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) # Уменьшаем задержку между попытками
|
||||
|
||||
Reference in New Issue
Block a user