Skip to content

Commit 10c275c

Browse files
committed
Fix voice STT task cleanup to properly await cancelled tasks
Problem: The _cleanup_tasks() method in OpenAISTTTranscriptionSession was only calling task.cancel() on pending tasks (listener, process_events, stream_audio, connection) but not awaiting them. This could lead to: 1. Unhandled task exception warnings 2. Potential resource leaks (websocket connections, file descriptors) 3. Improper cleanup of background tasks Evidence: - Similar to the recently fixed guardrail tasks cleanup (PR #1976) - Similar to the fixed websocket task cleanup (PR #1955) - asyncio best practices require awaiting cancelled tasks Solution: 1. Made _cleanup_tasks() async 2. Collect all real asyncio.Task objects that need to be awaited 3. Added await asyncio.gather() with return_exceptions=True to properly collect exceptions from cancelled tasks 4. Updated close() method to await _cleanup_tasks() Testing: - All existing voice/STT tests pass (17 passed) - Uses isinstance check to support mock objects in tests - Follows the same pattern as PR #1976 and PR #1955
1 parent 16169e1 commit 10c275c

File tree

1 file changed

+21
-2
lines changed

1 file changed

+21
-2
lines changed

‎src/agents/voice/models/openai_stt.py‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,18 +321,37 @@ def _check_errors(self) -> None:
321321
ifexcandisinstance(exc, Exception):
322322
self._stored_exception=exc
323323

324-
def_cleanup_tasks(self) ->None:
324+
asyncdef_cleanup_tasks(self) ->None:
325+
"""Cancel all pending tasks and wait for them to complete.
326+
327+
This ensures that any exceptions raised by the tasks are properly handled
328+
and prevents warnings about unhandled task exceptions.
329+
"""
330+
tasks= []
331+
325332
ifself._listener_taskandnotself._listener_task.done():
326333
self._listener_task.cancel()
334+
ifisinstance(self._listener_task, asyncio.Task):
335+
tasks.append(self._listener_task)
327336

328337
ifself._process_events_taskandnotself._process_events_task.done():
329338
self._process_events_task.cancel()
339+
ifisinstance(self._process_events_task, asyncio.Task):
340+
tasks.append(self._process_events_task)
330341

331342
ifself._stream_audio_taskandnotself._stream_audio_task.done():
332343
self._stream_audio_task.cancel()
344+
ifisinstance(self._stream_audio_task, asyncio.Task):
345+
tasks.append(self._stream_audio_task)
333346

334347
ifself._connection_taskandnotself._connection_task.done():
335348
self._connection_task.cancel()
349+
ifisinstance(self._connection_task, asyncio.Task):
350+
tasks.append(self._connection_task)
351+
352+
# Wait for all cancelled tasks to complete and collect exceptions
353+
iftasks:
354+
awaitasyncio.gather(*tasks, return_exceptions=True)
336355

337356
asyncdeftranscribe_turns(self) ->AsyncIterator[str]:
338357
self._connection_task=asyncio.create_task(self._process_websocket_connection())
@@ -367,7 +386,7 @@ async def close(self) -> None:
367386
ifself._websocket:
368387
awaitself._websocket.close()
369388

370-
self._cleanup_tasks()
389+
awaitself._cleanup_tasks()
371390

372391

373392
classOpenAISTTModel(STTModel):

0 commit comments

Comments
(0)