Skip to content

Commit 160bfce

Browse files
authored
AI-61: Strip unset None fields from ADK plugin payloads (#1442)
Use ToJsonOptions(exclude_unset=True) in the ADK plugin's payload converter, matching the OpenAI plugin's pattern. This reduces LlmRequest payloads from 4-8KB of mostly nulls to just the fields that were set.
1 parent 18cebd4 commit 160bfce

2 files changed

Lines changed: 50 additions & 5 deletions

File tree

temporalio/contrib/google_adk_agents/_plugin.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from temporalio.contrib.google_adk_agents._mcp import TemporalMcpToolSetProvider
1111
from temporalio.contrib.google_adk_agents._model import invoke_model
1212
from temporalio.contrib.pydantic import (
13-
PydanticPayloadConverter as _DefaultPydanticPayloadConverter,
13+
PydanticPayloadConverter,
14+
ToJsonOptions,
1415
)
1516
from temporalio.converter import DataConverter, DefaultPayloadConverter
1617
from temporalio.plugin import SimplePlugin
@@ -111,11 +112,16 @@ def _configure_data_converter(
111112
self, converter: DataConverter | None
112113
) -> DataConverter:
113114
if converter is None:
114-
return DataConverter(
115-
payload_converter_class=_DefaultPydanticPayloadConverter
116-
)
115+
return DataConverter(payload_converter_class=_AdkPayloadConverter)
117116
elif converter.payload_converter_class is DefaultPayloadConverter:
118117
return dataclasses.replace(
119-
converter, payload_converter_class=_DefaultPydanticPayloadConverter
118+
converter, payload_converter_class=_AdkPayloadConverter
120119
)
121120
return converter
121+
122+
123+
class _AdkPayloadConverter(PydanticPayloadConverter):
124+
"""PayloadConverter for Google ADK that strips unset None fields."""
125+
126+
def __init__(self) -> None:
127+
super().__init__(ToJsonOptions(exclude_unset=True))

tests/contrib/google_adk_agents/test_google_adk_agents.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Integration tests for ADK Temporal support."""
1616

17+
import json
1718
import logging
1819
import os
1920
import uuid
@@ -963,3 +964,41 @@ def supported_models(cls) -> list[str]:
963964
assert result.content is not None
964965
assert result.content.parts is not None
965966
assert result.content.parts[0].text == "hello from litellm"
967+
968+
969+
def test_unset_none_fields_stripped() -> None:
970+
"""ADK plugin converter strips unset None fields from Pydantic payloads."""
971+
plugin = GoogleAdkPlugin()
972+
converter = plugin._configure_data_converter(None)
973+
request = LlmRequest(
974+
model="gemini-2.0-flash",
975+
contents=[Content(parts=[Part(text="hello")])],
976+
)
977+
payloads = converter.payload_converter.to_payloads([request])
978+
serialized = json.loads(payloads[0].data)
979+
980+
assert serialized["model"] == "gemini-2.0-flash"
981+
assert "contents" in serialized
982+
for field in (
983+
"cache_config",
984+
"cache_metadata",
985+
"cacheable_contents_token_count",
986+
"previous_interaction_id",
987+
):
988+
assert field not in serialized, f"Unset field {field!r} should be stripped"
989+
990+
991+
def test_explicitly_set_none_preserved() -> None:
992+
"""Explicitly-set None is preserved (exclude_unset, not exclude_none)."""
993+
plugin = GoogleAdkPlugin()
994+
converter = plugin._configure_data_converter(None)
995+
request = LlmRequest(
996+
model="gemini-2.0-flash",
997+
contents=[Content(parts=[Part(text="hello")])],
998+
cache_config=None,
999+
)
1000+
payloads = converter.payload_converter.to_payloads([request])
1001+
serialized = json.loads(payloads[0].data)
1002+
1003+
assert "cache_config" in serialized, "Explicitly-set None should be preserved"
1004+
assert serialized["cache_config"] is None

0 commit comments

Comments
 (0)