Skip to content

Commit 652ce46

Browse files
committed
fix: preserve empty dict metadata instead of converting to None
- Change `if metadata` to `if metadata is not None` for truthiness check - Empty dict {} was incorrectly converted to None due to falsy check - Add test_empty_metadata_dict_not_converted_to_none to prevent regression
1 parent c28b499 commit 652ce46

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

src/google/adk/runners.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ async def run_async(
492492
"""
493493
run_config = run_config or RunConfig()
494494
# Create a shallow copy to isolate from caller's modifications
495-
metadata = metadata.copy() if metadata else None
495+
metadata = metadata.copy() if metadata is not None else None
496496

497497
if new_message and not new_message.role:
498498
new_message.role = 'user'

tests/unittests/test_runners.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,53 @@ def test_llm_request_without_metadata(self):
13751375

13761376
assert llm_request.metadata is None
13771377

1378+
@pytest.mark.asyncio
1379+
async def test_empty_metadata_dict_not_converted_to_none(self):
1380+
"""Test that empty dict {} is preserved and not converted to None."""
1381+
captured_metadata = None
1382+
1383+
def before_model_callback(callback_context, llm_request):
1384+
nonlocal captured_metadata
1385+
captured_metadata = llm_request.metadata
1386+
return LlmResponse(
1387+
content=types.Content(
1388+
role="model", parts=[types.Part(text="Test response")]
1389+
)
1390+
)
1391+
1392+
agent_with_callback = LlmAgent(
1393+
name="callback_agent",
1394+
model="gemini-2.0-flash",
1395+
before_model_callback=before_model_callback,
1396+
)
1397+
1398+
runner_with_callback = Runner(
1399+
app_name="test_app",
1400+
agent=agent_with_callback,
1401+
session_service=self.session_service,
1402+
artifact_service=self.artifact_service,
1403+
)
1404+
1405+
await self.session_service.create_session(
1406+
app_name=TEST_APP_ID, user_id=TEST_USER_ID, session_id=TEST_SESSION_ID
1407+
)
1408+
1409+
# Pass empty dict - should NOT become None
1410+
async for event in runner_with_callback.run_async(
1411+
user_id=TEST_USER_ID,
1412+
session_id=TEST_SESSION_ID,
1413+
new_message=types.Content(
1414+
role="user", parts=[types.Part(text="Hello")]
1415+
),
1416+
metadata={},
1417+
):
1418+
pass
1419+
1420+
# Empty dict should be preserved, not converted to None
1421+
assert captured_metadata is not None
1422+
assert captured_metadata == {}
1423+
assert isinstance(captured_metadata, dict)
1424+
13781425
@pytest.mark.asyncio
13791426
async def test_metadata_shallow_copy_isolation(self):
13801427
"""Test that shallow copy isolates top-level changes but shares nested objects."""

0 commit comments

Comments
 (0)