Skip to content

fix(telemetry): serialize Pydantic BaseModel in _safe_json_serialize#4651

Closed
giulio-leone wants to merge 1 commit intogoogle:mainfrom
giulio-leone:fix/safe-json-serialize-pydantic
Closed

fix(telemetry): serialize Pydantic BaseModel in _safe_json_serialize#4651
giulio-leone wants to merge 1 commit intogoogle:mainfrom
giulio-leone:fix/safe-json-serialize-pydantic

Conversation

@giulio-leone
Copy link

Problem

_safe_json_serialize silently replaces Pydantic BaseModel instances with the string '<not serializable>' when serializing tool responses for tracing spans.

Any tool that returns a Pydantic model rather than a plain dict executes correctly, but the traced output is lost:

tool_response = {'result': SearchResults(query='test', total=2, items=['doc1', 'doc2'])}
_safe_json_serialize(tool_response)
# Output: {"result": "<not serializable>"}
# Expected: {"result": {"query": "test", "total": 2, "items": ["doc1", "doc2"]}}

Root Cause

The default lambda in json.dumps unconditionally returns '<not serializable>' for any object that isn't natively JSON-serializable, including Pydantic models.

Fix

Replace the generic lambda with a _default function that calls model_dump(mode='json') for BaseModel instances before falling back to '<not serializable>'. BaseModel is already imported in tracing.py (line 63).

Test

56 telemetry tests pass, 2 consecutive clean runs.

Fixes #4629

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical data serialization problem within the telemetry system. Previously, Pydantic BaseModel objects were not properly converted to JSON, resulting in their replacement with a generic non-serializable string. The change introduces a custom default serialization handler that intelligently processes Pydantic models, thereby preserving valuable tracing information and enhancing the reliability of telemetry data.

Highlights

  • Pydantic BaseModel Serialization Fix: Addressed an issue where Pydantic BaseModel instances were not correctly serialized by _safe_json_serialize, leading to data loss in tracing spans.
  • Improved Telemetry Data: Ensured that tool responses containing Pydantic models are now fully serialized, providing complete and accurate data for telemetry and tracing.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/google/adk/telemetry/tracing.py
    • Modified the _safe_json_serialize function to correctly handle Pydantic BaseModel instances during JSON serialization.
Activity
  • No specific activity (comments, reviews, etc.) was provided in the context for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@google-cla
Copy link

google-cla bot commented Feb 28, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot
Copy link
Collaborator

adk-bot commented Feb 28, 2026

Response from ADK Triaging Agent

Hello @giulio-leone, thank you for creating this PR!

Before we can merge this, you'll need to sign the Contributor License Agreement (CLA). You can find more information in the "Details" link of the cla/google check at the bottom of the PR.

Also, could you please add a testing plan section to your PR description to describe how you tested your changes? This will help reviewers to review your PR more efficiently.

Thanks!

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly addresses the issue of serializing Pydantic BaseModel instances in _safe_json_serialize by introducing a custom default handler for json.dumps. The fix is well-targeted and effective.

My main feedback is that a very similar function, _safe_json_serialize_no_whitespaces in src/google/adk/telemetry/_experimental_semconv.py, has the same serialization problem but was not updated. To ensure consistency and prevent future bugs, I recommend applying the same fix there. To avoid code duplication, the new default handler logic could be extracted into a reusable, module-level function. I've left a specific comment with a suggestion on how to refactor this.

Comment on lines +119 to +126
def _default(o: Any) -> Any:
if isinstance(o, BaseModel):
return o.model_dump(mode='json')
return '<not serializable>'

try:
# Try direct JSON serialization first
return json.dumps(
obj, ensure_ascii=False, default=lambda o: '<not serializable>'
)
return json.dumps(obj, ensure_ascii=False, default=_default)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This correctly handles Pydantic model serialization. To improve reusability and fix a similar issue in another file, I suggest extracting the _default logic into a module-level function, for example _pydantic_json_default.

# At module level in tracing.py
def _pydantic_json_default(o: Any) -> Any:
  if isinstance(o, BaseModel):
    return o.model_dump(mode='json')
  return '<not serializable>'

A nearly identical function, _safe_json_serialize_no_whitespaces in src/google/adk/telemetry/_experimental_semconv.py, still uses the old lambda and will also need this fix. Making the helper function module-level will allow you to import and reuse it there, avoiding code duplication.

Suggested change
def _default(o: Any) -> Any:
if isinstance(o, BaseModel):
return o.model_dump(mode='json')
return '<not serializable>'
try:
# Try direct JSON serialization first
return json.dumps(
obj, ensure_ascii=False, default=lambda o: '<not serializable>'
)
return json.dumps(obj, ensure_ascii=False, default=_default)
try:
# Try direct JSON serialization first
return json.dumps(obj, ensure_ascii=False, default=_pydantic_json_default)

@giulio-leone giulio-leone force-pushed the fix/safe-json-serialize-pydantic branch from 99bd851 to 8cd9985 Compare February 28, 2026 14:38
_safe_json_serialize replaces Pydantic BaseModel instances with
'<not serializable>' because json.dumps cannot handle them natively.
Any tool that returns a Pydantic model has its traced output lost.

Replace the generic lambda default with a _default function that
calls model_dump(mode='json') for BaseModel instances before falling
back to '<not serializable>' for truly non-serializable objects.
BaseModel is already imported in tracing.py.

Fixes google#4629
@giulio-leone giulio-leone force-pushed the fix/safe-json-serialize-pydantic branch from 8cd9985 to 33c0d84 Compare February 28, 2026 14:39
@giulio-leone
Copy link
Author

Closing — CLA not yet signed. Will resubmit when ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

_safe_json_serialize drops Pydantic model tool results as '<not serializable>' in traces

2 participants