From 8b8a5b3dacb75a0a5ff90107ed541c35412b8b1d Mon Sep 17 00:00:00 2001 From: giulio-leone Date: Thu, 5 Mar 2026 16:43:14 +0100 Subject: [PATCH 1/2] fix: handle Pydantic BaseModel in _safe_json_serialize Add explicit BaseModel handling in _safe_json_serialize() in both tracing.py and lite_llm.py. Pydantic models are not directly JSON serializable, so we call model_dump() first to get a plain dict before serializing. This prevents '' telemetry data for Pydantic objects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/google/adk/models/lite_llm.py | 2 ++ src/google/adk/telemetry/tracing.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/google/adk/models/lite_llm.py b/src/google/adk/models/lite_llm.py index 8c1568ccc7..f4e0989f1a 100644 --- a/src/google/adk/models/lite_llm.py +++ b/src/google/adk/models/lite_llm.py @@ -493,6 +493,8 @@ def _safe_json_serialize(obj) -> str: """ try: + if isinstance(obj, BaseModel): + return json.dumps(obj.model_dump(), default=str) # Try direct JSON serialization first return json.dumps(obj, ensure_ascii=False) except (TypeError, OverflowError): diff --git a/src/google/adk/telemetry/tracing.py b/src/google/adk/telemetry/tracing.py index 707bc31396..42531ccf7f 100644 --- a/src/google/adk/telemetry/tracing.py +++ b/src/google/adk/telemetry/tracing.py @@ -117,6 +117,8 @@ def _safe_json_serialize(obj) -> str: """ try: + if isinstance(obj, BaseModel): + return json.dumps(obj.model_dump(), default=str) # Try direct JSON serialization first return json.dumps( obj, ensure_ascii=False, default=lambda o: '' From 637f045359a0e111396fb2e1bc8079f8d1838892 Mon Sep 17 00:00:00 2001 From: giulio-leone Date: Thu, 5 Mar 2026 18:07:18 +0100 Subject: [PATCH 2/2] refactor: extract Pydantic serialization into shared utility Extract the duplicated Pydantic BaseModel serialization logic from both lite_llm.py and tracing.py into a shared serialize_pydantic_model() helper in utils/serialization_utils.py. This ensures future changes to Pydantic serialization are made in a single place. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/google/adk/models/lite_llm.py | 7 +++-- src/google/adk/telemetry/tracing.py | 6 ++-- src/google/adk/utils/serialization_utils.py | 35 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/google/adk/utils/serialization_utils.py diff --git a/src/google/adk/models/lite_llm.py b/src/google/adk/models/lite_llm.py index f4e0989f1a..063cbfe7f9 100644 --- a/src/google/adk/models/lite_llm.py +++ b/src/google/adk/models/lite_llm.py @@ -51,6 +51,8 @@ from pydantic import Field from typing_extensions import override +from ..utils.serialization_utils import serialize_pydantic_model + from ..utils._google_client_headers import merge_tracking_headers from .base_llm import BaseLlm from .llm_request import LlmRequest @@ -493,8 +495,9 @@ def _safe_json_serialize(obj) -> str: """ try: - if isinstance(obj, BaseModel): - return json.dumps(obj.model_dump(), default=str) + serialized = serialize_pydantic_model(obj) + if serialized is not None: + return serialized # Try direct JSON serialization first return json.dumps(obj, ensure_ascii=False) except (TypeError, OverflowError): diff --git a/src/google/adk/telemetry/tracing.py b/src/google/adk/telemetry/tracing.py index 42531ccf7f..1093d3a796 100644 --- a/src/google/adk/telemetry/tracing.py +++ b/src/google/adk/telemetry/tracing.py @@ -65,6 +65,7 @@ from .. import version from ..utils.model_name_utils import is_gemini_model +from ..utils.serialization_utils import serialize_pydantic_model from ._experimental_semconv import is_experimental_semconv from ._experimental_semconv import maybe_log_completion_details from ._experimental_semconv import set_operation_details_attributes_from_request @@ -117,8 +118,9 @@ def _safe_json_serialize(obj) -> str: """ try: - if isinstance(obj, BaseModel): - return json.dumps(obj.model_dump(), default=str) + serialized = serialize_pydantic_model(obj) + if serialized is not None: + return serialized # Try direct JSON serialization first return json.dumps( obj, ensure_ascii=False, default=lambda o: '' diff --git a/src/google/adk/utils/serialization_utils.py b/src/google/adk/utils/serialization_utils.py new file mode 100644 index 0000000000..f2dc19420c --- /dev/null +++ b/src/google/adk/utils/serialization_utils.py @@ -0,0 +1,35 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Shared serialization utilities for Pydantic models.""" + +import json +from typing import Any +from typing import Optional + +from pydantic import BaseModel + + +def serialize_pydantic_model(obj: Any) -> Optional[str]: + """Serialize a Pydantic BaseModel to a JSON string. + + Args: + obj: The object to check and serialize. + + Returns: + A JSON string if the object is a Pydantic BaseModel, or None otherwise. + """ + if isinstance(obj, BaseModel): + return json.dumps(obj.model_dump(), default=str) + return None