primeira versão customizada de VAD- Voice Activity Detection
This commit is contained in:
@@ -155,6 +155,20 @@ async def send_json(
|
||||
)
|
||||
)
|
||||
|
||||
async def close_optional_provider(provider: object | None) -> None:
|
||||
"""
|
||||
Close providers exposing an optional asynchronous cleanup hook.
|
||||
|
||||
Providers without aclose() require no action.
|
||||
"""
|
||||
if provider is None:
|
||||
return
|
||||
|
||||
close_method = getattr(provider, "aclose", None)
|
||||
|
||||
if callable(close_method):
|
||||
await close_method()
|
||||
|
||||
|
||||
async def forward_stt_events(
|
||||
websocket: WebSocket,
|
||||
@@ -331,13 +345,29 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
"Turn finalization failed. session_id=%s turn_id=%s",
|
||||
session.session_id,
|
||||
turn_id,
|
||||
)
|
||||
|
||||
if turn_id not in interrupting_turns:
|
||||
try:
|
||||
await send_json(
|
||||
websocket,
|
||||
error_event(
|
||||
"TURN_EXECUTION_FAILED",
|
||||
str(exc),
|
||||
session_id=session.session_id,
|
||||
turn_id=turn_id,
|
||||
),
|
||||
)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to report turn execution failure."
|
||||
)
|
||||
|
||||
async def cancel_finalization_task() -> None:
|
||||
"""
|
||||
Cancel and await the Gateway-owned finalization task.
|
||||
@@ -792,7 +822,6 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# audio.stop
|
||||
# ---------------------------------------------------------
|
||||
@@ -805,37 +834,41 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
"STT session has not been started."
|
||||
)
|
||||
|
||||
if pending_audio_chunk is not None:
|
||||
raise ValueError(
|
||||
"audio.stop received while an audio chunk is pending."
|
||||
)
|
||||
|
||||
if agent is None:
|
||||
raise RuntimeError(
|
||||
"Agent adapter is not initialized."
|
||||
)
|
||||
|
||||
if tts is None:
|
||||
raise RuntimeError(
|
||||
"TTS adapter is not initialized."
|
||||
)
|
||||
|
||||
if pipeline is None:
|
||||
raise RuntimeError(
|
||||
"Voice pipeline is not initialized."
|
||||
)
|
||||
|
||||
# Finish the STT session normally.
|
||||
await stt.stop_session()
|
||||
await pipeline.wait_turn_task()
|
||||
stt_events_task = None
|
||||
|
||||
try:
|
||||
pipeline.complete_turn(request.turn_id)
|
||||
except (RuntimeError, ValueError) as exc:
|
||||
if agent is None or tts is None:
|
||||
raise RuntimeError(
|
||||
"Turn pipeline did not complete successfully."
|
||||
) from exc
|
||||
"Agent or TTS adapter is not initialized."
|
||||
)
|
||||
|
||||
if not pipeline.is_active_turn(request.turn_id):
|
||||
raise ValueError(
|
||||
f"Unexpected turn_id={request.turn_id}."
|
||||
)
|
||||
|
||||
if pending_audio_chunk is not None:
|
||||
raise ValueError(
|
||||
"audio.stop received while an audio chunk is pending."
|
||||
)
|
||||
|
||||
if (
|
||||
finalization_task is not None
|
||||
and not finalization_task.done()
|
||||
):
|
||||
raise RuntimeError(
|
||||
"Turn finalization is already running."
|
||||
)
|
||||
|
||||
await stt.stop_session()
|
||||
|
||||
# Do not await OCI/TTS here.
|
||||
# Keep the WebSocket receive loop available.
|
||||
finalization_task = asyncio.create_task(
|
||||
finalize_turn(request.turn_id)
|
||||
)
|
||||
|
||||
except (ValidationError, RuntimeError, ValueError) as exc:
|
||||
await send_json(
|
||||
@@ -847,20 +880,10 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
turn_id=payload.get("turn_id"),
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
||||
stt = None
|
||||
pending_audio_chunk = None
|
||||
|
||||
await send_json(
|
||||
websocket,
|
||||
TurnEnded(
|
||||
session_id=session.session_id,
|
||||
turn_id=request.turn_id,
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# interrupt
|
||||
# ---------------------------------------------------------
|
||||
@@ -895,8 +918,11 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
# Only cancel resources belonging to the requested active turn.
|
||||
if pipeline.is_active_turn(request.turn_id):
|
||||
try:
|
||||
# Cancel and await STT/Agent/TTS execution before
|
||||
# confirming the interruption to the client.
|
||||
async with turn_transition_lock:
|
||||
interrupting_turns.add(request.turn_id)
|
||||
|
||||
await cancel_finalization_task()
|
||||
|
||||
await pipeline.cancel_turn_task()
|
||||
|
||||
# Abort transcription without requesting a final result.
|
||||
@@ -923,8 +949,19 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
continue
|
||||
|
||||
else:
|
||||
# Preserve the existing tolerant behavior for unknown turns.
|
||||
pipeline.interrupt_if_active(request.turn_id)
|
||||
await send_json(
|
||||
websocket,
|
||||
error_event(
|
||||
"INVALID_TURN",
|
||||
(
|
||||
f"Turn {request.turn_id} "
|
||||
"is no longer active."
|
||||
),
|
||||
session_id=session.session_id,
|
||||
turn_id=request.turn_id,
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
||||
await send_json(
|
||||
websocket,
|
||||
@@ -946,6 +983,12 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
raise RuntimeError(
|
||||
"Voice pipeline is not initialized."
|
||||
)
|
||||
# Prevent normal completion during session shutdown.
|
||||
async with turn_transition_lock:
|
||||
if session.active_turn_id is not None:
|
||||
interrupting_turns.add(session.active_turn_id)
|
||||
|
||||
await cancel_finalization_task()
|
||||
|
||||
# Cancel and await any running STT/Agent/TTS task.
|
||||
await pipeline.cancel_turn_task()
|
||||
@@ -1079,3 +1122,15 @@ async def audio_socket(websocket: WebSocket) -> None:
|
||||
session.session_id if session else None,
|
||||
)
|
||||
|
||||
# Release provider-owned HTTP connections.
|
||||
if agent is not None:
|
||||
try:
|
||||
await close_optional_provider(agent)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to close Agent provider. session_id=%s",
|
||||
session.session_id if session else None,
|
||||
)
|
||||
finally:
|
||||
agent = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user