`GoogleSearchAgentTool` parallel responses fail to serialize deferred `GroundingMetadata` (`MockValSer`)
- Vorherrschende Sprache
- Python
- Sterne
- 21.5k
- Forks
- 4k
- Ø Merge
- 1 T. 14 Std.
- Gemergte PRs (30 T.)
- 37
Beschreibung
## 🔴 Required Information
**Describe the Bug:**
`AgentTool(propagate_grounding_metadata=True)`—which `GoogleSearchAgentTool`
enables—stores a raw `google.genai.types.GroundingMetadata` instance in session
state under `temp:_adk_grounding_metadata`. That value reaches
`EventActions.state_delta`, which is typed as `dict[str, Any]`, so event
serialization relies on Pydantic's duck-typed serialization of the value.
`google-genai` 2.18.0 and newer configure their generated models with
`defer_build=True`. A `GroundingMetadata` produced by nested validation (for
example, as part of `Candidate` validation, as happens for model responses)
leaves `GroundingMetadata.__pydantic_serializer__` as a `MockValSer`.
Serializing the event then raises:
```text
pydantic_core._pydantic_core.PydanticSerializationError: Error calling function `_serialize_state_delta`: TypeError: 'MockValSer' object is not an instance of 'SchemaSerializer'
```
The `_make_json_serializable` fallback introduced by #4748 does not recover
from this case. `pydantic_core.to_jsonable_python(value,
serialize_unknown=True)` raises the same `TypeError` before
`serialize_unknown` can apply, so the exception escapes
`_serialize_state_delta`.
This surfaces in `merge_parallel_function_response_events` in
`google/adk/flows/llm_flows/functions.py`, which calls
`event.actions.model_dump(exclude_none=True, by_alias=True)`. When a search call
runs in parallel with another function call, the merge raises and aborts the
invocation.
This is related to #4748 and prior grounding-metadata persistence reports such
as #5840, but it is a distinct failure mode: the value is a valid Pydantic
model whose deferred serializer has not been built.
**Steps to Reproduce:**
1. Install the affected versions:
```bash
python -m pip install "google-adk==2.7.1" "google-genai==2.19.0" "pydantic==2.13.4"
```
2. Run the snippet under *Minimal Reproduction Code* below. It requires no
model call or API key.
3. Observe the `PydanticSerializationError` above.
**Expected Behavior:**
Serializing an event whose `state_delta` contains grounding metadata propagated
by ADK succeeds, and parallel function-response merging preserves both tool
results. ADK-created state should not depend on whether a deferred class
serializer happened to be built earlier in the process.
**Observed Behavior:**
`EventActions.model_dump(...)` raises from
`merge_parallel_function_response_events`, and the invocation terminates
without a final response.
**Environment Details:**
- ADK Library Version: 2.7.1 (also reproduced on 2.7.0)
- Desktop OS: macOS 26.6 (also reproduced on Linux)
- Python Version: 3.14.6 (also reproduced on 3.12.6)
- `google-genai`: 2.19.0 (also reproduced on 2.18.1)
- `pydantic`: 2.13.4 / `pydantic-core`: 2.46.4
**Model Information:**
- Are you using LiteLLM: No
- Which model is being used: N/A—the minimal reproduction performs no model
call and requires no API key.
---
## 🟡 Optional Information
**Regression:**
This is a `google-genai` 2.18.0 regression:
- `google-genai==2.17.0`: nested `GroundingMetadata` has a built
`SchemaSerializer`; the merge succeeds.
- `google-genai==2.18.0`: the shared model config includes `defer_build=True`;
nested `GroundingMetadata` retains a `MockValSer`; the merge fails.
The failure also reproduces with `google-adk==2.6.0` plus
`google-genai==2.18.1`, so it is not specific to ADK 2.7.x. ADK is affected
because it propagates the raw deferred model through `state_delta` and then
serializes that state while merging parallel tool responses.
**Logs:**
```text
Failed to serialize `state_delta`; some values are not JSON-serializable and
will be replaced with a string representation in the persisted event.
Traceback (most recent call last):
File "google/adk/events/event_actions.py", line 106, in _serialize_state_delta
return cast(dict[str, Any], handler(value))
TypeError: 'MockValSer' object is not an instance of 'SchemaSerializer'
During fallback handling, `to_jsonable_python(...)` raises the same TypeError.
The resulting PydanticSerializationError escapes the parallel-response merge.
```
**Additional Context:**
Two details make this easy to miss in tests:
- Constructing `types.GroundingMetadata(...)` directly builds the serializer as
a side effect. Nested validation via `types.Candidate.model_validate(...)`,
which mirrors the response-validation path, leaves the `MockValSer`.
- Once anything else in the process builds this class serializer, the failure
disappears for subsequent calls.
Possible fixes, in rough order of narrowness:
1. Build `types.GroundingMetadata` before ADK propagates or serializes it, for
example with `types.GroundingMetadata.model_rebuild(force=True)`. Removing
`defer_build` for this model in `google-genai` would also resolve the root
behavior.
2. Store grounding metadata in `state_delta` in serialized form, then
revalidate it where `base_llm_flow` attaches it to the response.
3. Make `_make_json_serializable` resilient per value so the fallback cannot
itself abort event serialization. This is defense in depth and would
degrade the metadata rather than preserve it.
Happy to send a PR for the preferred direction.
**Minimal Reproduction Code:**
```python
from google.adk.events import Event, EventActions
from google.adk.flows.llm_flows.functions import (
merge_parallel_function_response_events,
)
from google.genai import types
candidate = types.Candidate.model_validate(
{"groundingMetadata": {"webSearchQueries": ["some query"]}}
)
print(type(types.GroundingMetadata.__pydantic_serializer__).__name__)
# MockValSer
def response_event(name: str, state_delta: dict) -> Event:
return Event(
invocation_id="inv",
author="my_agent",
content=types.Content(
role="user",
parts=[
types.Part.from_function_response(
name=name,
response={"ok": True},
)
],
),
actions=EventActions(state_delta=state_delta),
)
events = [
response_event(
"search_agent",
{"temp:_adk_grounding_metadata": candidate.grounding_metadata},
),
response_event("other_tool", {"temp:other": 1}),
]
merge_parallel_function_response_events(events)
# PydanticSerializationError
```
The fallback also fails directly:
```python
from pydantic_core import to_jsonable_python
to_jsonable_python(
{"k": candidate.grounding_metadata},
serialize_unknown=True,
)
# TypeError: 'MockValSer' object is not an instance of 'SchemaSerializer'
```
**How often has this issue occurred?:**
- Always (100%) when a deferred `GroundingMetadata` is present in one of two
or more function-response events being merged.
Beitragsleitfaden
Bewertung
Dieses Issue wurde noch nicht bewertet.