Skip to content

fix: send LiteLLM tool results that hold dates or Decimals as JSON - #8125

Open
ravismash wants to merge 1 commit into
google:mainfrom
ravismash:fix/litellm-tool-result-json
Open

ravismash wants to merge 1 commit into
google:mainfrom
ravismash:fix/litellm-tool-result-json

Conversation

@ravismash

Copy link
Copy Markdown

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

2. Or, if no issue exists, describe the change:

Problem:
_safe_json_serialize in models/lite_llm.py falls back to str() of the whole payload when json.dumps hits a value it cannot encode. A tool result with one date, Decimal, UUID, Enum, set, bytes or Pydantic model therefore reaches the model as a Python repr ({'due_date': datetime.date(2026, 12, 1), 'total': Decimal('12.50')}). An output_schema agent wrapped in AgentTool returns such values for any date or Decimal field.

Solution:
When json.dumps fails, convert the payload with pydantic_core.to_jsonable_python(obj, by_alias=False, exclude_none=True, bytes_mode="base64"), the same options the Gemma path uses since #7668, and only then fall back to str(). Payloads that json.dumps already handles are unchanged. Unknown objects and circular dicts still fall back to str(), so the existing tests for those cases keep passing.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Added to tests/unittests/models/test_litellm.py:

  • test_safe_json_serialize_converts_values_json_cannot_encode (date, Decimal, Enum, UUID, nested Pydantic model, None)
  • test_content_to_message_param_function_response_with_date_is_json
$ pytest tests/unittests/models/test_litellm.py -q
818 passed, 3 warnings in 8.04s

Manual End-to-End (E2E) Tests:

Script (no API key needed; fake models, real ADK runner and the real LiteLLM converter): a child LlmAgent with output_schema holding date, Decimal and Enum fields is called by a parent through AgentTool, and the function response event is passed to _content_to_message_param.

Before (google-adk 2.14.0):

LiteLLM tool message content sent to the model: {'due_date': datetime.date(2026, 12, 1), 'total': Decimal('12.50'), 'status': 'paid'}

After (this branch, macOS, Python 3.12):

LiteLLM tool message content sent to the model: {"due_date": "2026-12-01", "total": "12.50", "status": "paid"}

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

Formatting checked with pyink 25.12 and isort using the repo's pyproject settings.

_safe_json_serialize fell back to str() for the whole payload when json.dumps
hit a value it cannot encode, so a tool result with one date, Decimal, UUID,
Enum, set, bytes or Pydantic model reached the model as a Python repr. An
output_schema agent wrapped in AgentTool always returns such values for date
and Decimal fields. Convert them with pydantic_core.to_jsonable_python, as the
Gemma path does since google#7668, and keep str() only for payloads Pydantic cannot
serialize.
@google-cla

google-cla Bot commented Nov 13, 2026

Copy link
Copy Markdown

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.

@ravismash

Copy link
Copy Markdown
Author

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.

@googlebot I signed it!

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.

LiteLlm sends tool results that hold a date, Decimal or Pydantic model to the model as a Python repr

2 participants