Repository navigation
Conversation
OpenAILlm sent a tool result with a bare json.dumps. A tool that returns a datetime, bytes or a Pydantic model made the next request raise TypeError, so the run failed on the first tool turn. ADK's own load_memory tool returns such a model, so every OpenAILlm agent that uses it failed. The same result sent to Gemini works. json.dumps now gets a default that converts these values with pydantic_core.to_jsonable_python, as the Gemma fix does: ISO datetimes, base64 bytes, models as dicts with None fields dropped and field names not aliased, and str() as a last resort. The default only runs on values that raised before, so results that already worked are sent unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
load_memory) #7667 / fix(models): stop Gemma 3 failing on datetime or Pydantic tool results #7668 (the same crash, fixed for Gemma 3)2. Or, if no issue exists, describe the change:
Problem:
OpenAILlmbuilds each tool message with a barejson.dumps(part.function_response.response)(_openai_llm.py:147). Tool results reach the adapter as Python objects:_caller.pywraps a non-dict result as{'result': value}and keeps it as is. So when a tool returns adatetime,bytesor a Pydantic model, the next request raisesTypeErrorand the run stops.ADK's own
load_memorytool returns a Pydantic model (LoadMemoryResponse), so everyOpenAILlmagent that usesload_memoryfails on the turn after the tool runs. The same agent works on Gemini.Repro (a real
InMemoryRunner,load_memory_toolandOpenAILlm; onlychat.completions.createis stubbed):On main:
A tool that returns
{"created": datetime(...)}fails the same way:TypeError: Object of type datetime is not JSON serializable.Solution:
Pass a
default=hook to thatjson.dumps. The hook callspydantic_core.to_jsonable_pythonwith the same settings as the Gemma fix (#7668):by_alias=False,exclude_none=True,bytes_mode="base64",serialize_unknown=True. So datetimes become ISO 8601 text, bytes become base64 text, models become dicts of their non-Nonefields, and values Pydantic cannot convert fall back to theirstr().json.dumpsonly callsdefaultfor values it cannot encode itself, so every tool result that works today is sent byte for byte the same (sameensure_ascii, same layout). Only results that used to raise change. The siblingOpenAIResponsesLlmin the same package already passesdefault=str(_openai_responses_llm.py:169-170).With this change the repro prints:
Testing Plan
Unit Tests:
New parametrized test
test_content_to_openai_messages_encodes_tool_result_valuesintests/unittests/integrations/openai/test_openai_llm.py, with one case each for adatetime,bytes, a Pydantic model, aLoadMemoryResponse, and a plain JSON result (non-ASCII text and a nestedNone) whose output must stay exactly whatjson.dumpsgives today.Without the source change, the four non-JSON cases fail with their own
TypeErrorand the plain case passes (4 failed, 66 passed). With it:pyink and isort leave both files unchanged.
Manual End-to-End (E2E) Tests:
I don't have an OpenAI key, so the repro above stands in for a live run: a real
InMemoryRunner,InMemoryMemoryService,load_memory_toolandOpenAILlm, withchat.completions.createstubbed to return realChatCompletionobjects. Its output on main and with this change is shown above.Checklist
Additional context
Values
json.dumpsrejects without callingdefault(dict keys that are notstr,int,float,boolorNone, and circular references) still raise as before; that is out of scope here.