google / google/adk-python

AsyncClient.aclose() task destroyed during cleanup - Missing LLM client cleanup in Runner.close()

Đang mở
#3,550 4 bình luận 0 reaction 2 người được giao Được @junseon-yoo nhận Xem trên GitHub
core needs review planned
Ngôn ngữ chính
Python
Star
21.5k
Fork
4k
Merge trung bình
1 ngày 14 giờ
Pull request đã merge (30 ngày)
37

Mô tả

**Describe the bug**

When running ADK agents in production with `google-genai>=1.50.1`, I'm seeing recurring asyncio errors about tasks being destroyed while pending during cleanup. The error happens when the genai `AsyncClient.aclose()` method is called during garbage collection.

This occurs because the `Gemini` class uses `@cached_property` for `api_client` and `_live_api_client`, but these clients are never explicitly closed in `Runner.close()`. Starting from genai 1.50.1, custom httpx clients won't be closed automatically, so when the Gemini instance is garbage collected, the aclose() task starts but gets destroyed before completion.

**To Reproduce**

Steps to reproduce the behavior:

1. Install `google-adk==1.18.0` and `google-genai==1.50.1`
2. Run the following minimal code:

```python
"""
Minimal reproduction for ADK Issue #3550

Bug: AsyncClient.__del__ creates orphaned asyncio tasks when Runner.close()
is not called before event loop shutdown.

Setup environment variables:
export GOOGLE_GENAI_USE_VERTEXAI=true
export GOOGLE_CLOUD_PROJECT="your-project-id"
export GOOGLE_CLOUD_LOCATION="your-location"

"""
import asyncio
import gc

from google.adk.agents import LlmAgent
from google.adk.models.google_llm import Gemini
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types

async def test():
agent = LlmAgent(
model=Gemini(model="gemini-2.0-flash-exp"),
name="test"
)

runner = Runner(
app_name="test",
agent=agent,
session_service=InMemorySessionService()
)

session = await runner.session_service.create_session(
app_name="test",
user_id="user1"
)

content = types.Content(
role='user',
parts=[types.Part(text='hello')]
)

async for event in runner.run_async(
user_id="user1",
session_id=session.id,
new_message=content
):
pass

# Bug: NOT calling await runner.close()
del runner
gc.collect()

if __name__ == '__main__':
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.set_debug(True)

loop.run_until_complete(test())

pending = asyncio.all_tasks(loop)
print(f"\nOrphaned tasks: {len(pending)}")

loop.close()
```

3. Run the script
4. Error stacktrace:

```
Orphaned tasks: 2
Task was destroyed but it is pending!
source_traceback: Object created at (most recent call last):
```

**Expected behavior**

`Runner.close()` should clean up all resources including LLM client connections, and no asyncio warnings should appear during cleanup.

**Screenshots**

N/A

**Desktop (please complete the following information):**

- OS: Linux (production), macOS (local)
- Python version: Python 3.11
- ADK version: 1.18.0

**Model Information:**

- Are you using LiteLLM: No
- Which model is being used: gemini-2.5-flash

**Additional context**

The issue is in `runners.py:1298-1300` where `Runner.close()` only cleans up toolsets:

```python
async def close(self):
"""Closes the runner."""
await self._cleanup_toolsets(self._collect_toolset(self.agent))
# Missing: LLM client cleanup
```

Root cause in `google/adk/models/google_llm.py`:
- Line 196-208: `api_client` cached property is never closed
- Line 240-246: `_live_api_client` cached property is never closed

Related to #1112 and #2804 but specifically affects LLM clients rather than MCP toolsets.

Workaround attempts that didn't work:
- Upgrading genai 1.49.0 → 1.50.1
- Adding shutdown cleanup in FastAPI (only fixes app shutdown, not per-request cleanup)
- Log filtering (masks the symptom)

Proposed fix following the existing `_cleanup_toolsets` pattern:
1. Add `BaseLlm.aclose()` method (default no-op for backward compatibility)
2. Implement `Gemini.aclose()` to close cached clients
3. Add `Runner._collect_llm_models()` to gather LLM instances
4. Add `Runner._cleanup_llm_models()` to close them
5. Update `Runner.close()` to cleanup both toolsets and LLM clients

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.