fix(adk): return str from serialize_metadata_value for pydantic values#2173
fix(adk): return str from serialize_metadata_value for pydantic values#2173anxkhn wants to merge 1 commit into
Conversation
serialize_metadata_value is annotated -> str and its docstring promises a string representation, but for values with a model_dump attribute it returned value.model_dump(...), which pydantic produces as a dict. The result is stored into A2A message/event metadata alongside sibling values that are strings (for example error_code is stringified explicitly), so a dict was injected where a string was expected for usage_metadata, grounding_metadata, and custom_metadata. Wrap the model_dump call in str() so the return matches the annotation and docstring. This mirrors the equivalent helper in the langgraph package, which already wraps model_dump in str(). Add a regression test asserting a pydantic value serializes to a str. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes serialize_metadata_value in the ADK event converter so it consistently returns a str for Pydantic model inputs, aligning behavior with its type annotation/docstring and avoiding injecting dict objects into A2A event metadata.
Changes:
- Wrap
value.model_dump(...)withstr(...)when serializing Pydantic values. - Add unit tests covering Pydantic-model and plain-value serialization behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| python/packages/kagent-adk/src/kagent/adk/converters/event_converter.py | Ensures Pydantic model_dump() output is coerced to str to match function contract and metadata expectations. |
| python/packages/kagent-adk/tests/unittests/converters/test_event_converter.py | Adds tests validating string output for both Pydantic and non-Pydantic inputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from a2a.types import TaskState, TaskStatusUpdateEvent | ||
| from google.genai import types as genai_types | ||
| from kagent.core.a2a import get_kagent_metadata_key | ||
| from pydantic import BaseModel | ||
|
|
| """A pydantic value must serialize to a str, matching the -> str | ||
| annotation and docstring, so metadata values stay strings like their | ||
| siblings (e.g. error_code, which is stringified explicitly).""" |
supreme-gg-gg
left a comment
There was a problem hiding this comment.
There's nothing wrong with returning dict here. A2A metadata allows them and they are needed in the frontend to properly show things like token usage:
kagent/ui/src/lib/messageHandlers.ts
Lines 390 to 398 in 3eff08b
A better fix would be to update the type annotation to str | dict[str, Any] and the docstring.
If you trace the langgraph example you cited, you will find that even though serialize_metadata_value does return string it is in fact never used in the converter with any metadata, so it's effectively dead code.
What
serialize_metadata_valueinpython/packages/kagent-adk/src/kagent/adk/converters/event_converter.pyis declared
def serialize_metadata_value(value: Any) -> str:and its docstringpromises to return the "String representation of the value." For any value that
has a
model_dumpattribute (that is, any Pydantic model) it did:Pydantic's
model_dump()returns adict, not a string (the string form ismodel_dump_json()), so the function violated both its-> strannotation andits docstring for Pydantic inputs.
This is not purely cosmetic. The result is written into A2A message/event
metadata by
_get_context_metadata, next to sibling values that are plainstrings (for example
error_codeis stored viastr(...)). Real Pydanticvalues flow through this branch:
event.usage_metadata,event.grounding_metadata,and
event.custom_metadata. So adictwas being injected where the surroundingmetadata values are strings.
Fix
Wrap the
model_dump(...)call instr()so the return matches the annotationand the docstring:
This also makes the helper consistent with its twin in the langgraph package,
python/packages/kagent-langgraph/src/kagent/langgraph/_metadata_utils.py, whichalready wraps
model_dump(...)instr(...). The adk copy had simply droppedthe
str()wrapper. No signature or API change; theexceptfallback alreadyreturned
str(value).Tests
Added
TestSerializeMetadataValueinpython/packages/kagent-adk/tests/unittests/converters/test_event_converter.py:strequal tostr(model.model_dump(exclude_none=True, by_alias=True))(fails on the oldcode, which returned a dict; passes after the wrap), and
42) serializes to"42".cd python && uv run pytest packages/kagent-adk/tests/unittests/-> 340 passed,1 skipped.
ruff formatandruff checkare clean on the changed files.