fix(google): release the genai client when the realtime session closes - #6643
Merged
Merged
Conversation
RealtimeSession creates its own genai Client but aclose() only tore down the websocket, so the http clients stayed alive until the collector ran AsyncClient.__del__ - which does asyncio.get_running_loop().create_task(self.aclose()), creating pending tasks on whatever event loop happens to be running at that moment. In CI this surfaces as unrelated tests failing the leaked-task check.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
google.realtime.RealtimeSessionconstructs its owngenai.Client(realtime_api.py:488), butaclose()only tears down the websocket and the pending futures — the client is never closed. Its http clients therefore stay alive until the garbage collector reaches them, and bothgoogle/genai/client.py:186andgoogle/genai/_api_client.py:2234implement__del__as:So the close is scheduled as a task on whatever event loop happens to be running when the collector fires — not necessarily the loop the client was created on, and possibly during shutdown.
The visible symptom is in CI: the
unit-testsjob intermittently fails thefail_on_leaked_taskscheck with pendingBaseApiClient.aclose/AsyncClient.aclose/ httpxAsyncClient.aclosetasks, attributed to whichever test happened to be finishing when the collector ran (I hit it on an unrelated AMD-only PR, #6639).Fix
RealtimeSession.aclose()now awaitsself._client.aio.aclose()after the rest of its teardown, so the session releases what it allocated. Failures there are logged rather than raised, since aclose is a teardown path.tests/test_plugin_google_realtime.pybuilds sessions through an async context manager that closes them, instead of leaving them to the collector — otherwise the tests keep leaking clients regardless of the plugin fix. Added a test asserting the close actually reaches the genai client.Verification
ruff format --check,ruff check,check_types.py(mypy strict) and the fullpytest --unitsuite all pass locally.