Skip to content

Commit e32f498

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 db12fd1 commit e32f498

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
@@ -534,7 +534,7 @@ async def run_async(
534534
"""
535535
run_config = run_config or RunConfig()
536536
# Create a shallow copy to isolate from caller's modifications
537-
metadata = metadata.copy() if metadata else None
537+
metadata = metadata.copy() if metadata is not None else None
538538

539539
if new_message and not new_message.role:
540540
new_message.role = 'user'

tests/unittests/test_runners.py

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

13771377
assert llm_request.metadata is None
13781378

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

0 commit comments

Comments
 (0)