implementacao de interrupcao em audio streming

This commit is contained in:
2026-10-16 01:15:34 +00:00
parent 19a415daae
commit 2449b34137
12 changed files with 2119 additions and 85 deletions

View File

@@ -281,12 +281,110 @@ async def audio_socket(websocket: WebSocket) -> None:
stt: STTAdapter | None = None
stt_events_task: asyncio.Task | None = None
finalization_task: asyncio.Task[None] | None = None
turn_transition_lock = asyncio.Lock()
interrupting_turns: set[int] = set()
completed_turns: set[int] = set()
turn_manager = create_turn_manager()
agent: AgentAdapter | None = None
tts: TTSAdapter | None = None
pipeline: VoicePipeline | None = None
async def finalize_turn(
turn_id: int,
) -> None:
"""
Complete a turn without blocking WebSocket message reception.
Normal completion must not race with interruption.
"""
if pipeline is None or session is None:
return
try:
await pipeline.wait_turn_task()
async with turn_transition_lock:
if turn_id in interrupting_turns:
return
if turn_id in completed_turns:
return
if not pipeline.is_active_turn(turn_id):
return
pipeline.complete_turn(turn_id)
completed_turns.add(turn_id)
await send_json(
websocket,
TurnEnded(
session_id=session.session_id,
turn_id=turn_id,
),
)
except asyncio.CancelledError:
raise
except Exception:
logger.exception(
"Turn finalization failed. session_id=%s turn_id=%s",
session.session_id,
turn_id,
)
async def cancel_finalization_task() -> None:
"""
Cancel and await the Gateway-owned finalization task.
This task is separate from the execution task owned
by VoicePipeline.
"""
nonlocal finalization_task
task = finalization_task
if task is None:
return
if task is asyncio.current_task():
raise RuntimeError(
"Finalization task cannot cancel itself."
)
if not task.done():
task.cancel()
try:
await asyncio.wait_for(
asyncio.shield(task),
timeout=5.0,
)
except asyncio.CancelledError:
current = asyncio.current_task()
if current is not None and current.cancelling():
raise
if not task.cancelled():
raise
except TimeoutError:
logger.error(
"Finalization task cancellation timed out. "
"session_id=%s",
session.session_id if session else None,
)
finally:
if task.done() and finalization_task is task:
finalization_task = None
try:
while True:
message = await websocket.receive()
@@ -729,7 +827,6 @@ async def audio_socket(websocket: WebSocket) -> None:
# Finish the STT session normally.
await stt.stop_session()
await pipeline.wait_turn_task()
stt_events_task = None
@@ -904,6 +1001,15 @@ async def audio_socket(websocket: WebSocket) -> None:
pass
finally:
# Stop Gateway-owned finalization before provider cleanup.
try:
await cancel_finalization_task()
except Exception:
logger.exception(
"Failed to cancel finalization task. session_id=%s",
session.session_id if session else None,
)
# Cancel and await the task owned by VoicePipeline.
if pipeline is not None:
try: