Skip to content

OpenAIResponsesLlm drops the keys beside "result" in a tool's dict return #7836

Closed

Description

@adityasingh2400

🔴 Required Information

Describe the Bug:
OpenAIResponsesLlm drops every key except result from a tool's dict return. _serialize_json_value (integrations/openai/_openai_responses_llm.py) unwraps any mapping that has a non-None result key and sends only that value as the function_call_output. ADK wraps a non-dict tool return as exactly {"result": value}, but a tool's own dict can also have a result key next to other data. For example, {"result": "found 2 contracts", "contracts": [...], "next_page_token": "abc"} reaches the model as just found 2 contracts.

AnthropicLlm had the same bug, fixed in #7073: it now unwraps only when the keys are exactly {"result"}.

Steps to Reproduce:

  1. Give an LlmAgent using OpenAIResponsesLlm(model="gpt-5") a function tool that returns {"result": "found 2 contracts", "contracts": ["NDA-12", "MSA-7"], "next_page_token": "abc"}.
  2. Let the model call it and inspect the function_call_output item in the next responses.create input.

Expected Behavior:
The whole dict is sent as JSON. Only the bare {"result": value} wrapper is unwrapped.

Observed Behavior:

function_call_output sent to OpenAI: found 2 contracts

Environment Details:

  • ADK Library Version (pip show google-adk): 2.13.0 (also on main at 75fb788)
  • Desktop OS: macOS
  • Python Version (python -V): 3.11.16

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: gpt-5 via OpenAIResponsesLlm

🟡 Optional Information

Additional Context:

if 'result' in value and value['result'] is not None:
  result = value['result']
  if isinstance(result, str):
    return result
  return json.dumps(result, default=str)

How often has this issue occurred?:

  • Always (100%), for tool dicts that have a result key beside other keys

Activity

  1. added
    models[Component] This issue is related to model support
    on Oct 30, 2026
  2. surajksharma07 commented on Oct 30, 2026

    @surajksharma07
    Collaborator

    @adityasingh2400 Reproduced this on 2.13.0 and main: only “found 2 contracts” reaches the model while contracts/next_page_token get dropped.

    A workaround that worked locally: unwrap only when value.keys() == {'result'} in _serialize_json_value (the same rule AnthropicLlm got in #7073) so any other dict goes out as full JSON. Could you give that a try on your side and check that the bare {"result": x} returns and the MCP content outputs still behave the same?

    Once you’ve confirmed it holds up #7837 looks like the right place for it. Will flag it for review. #7847 looks like it covers the same change so it’s worth settling on one of the two.

  3. adityasingh2400 commented on Oct 31, 2026

    @adityasingh2400
    ContributorAuthor

    Thanks for reproducing it. #7837 already uses exactly that rule (value.keys() == {'result'}), and I re-checked both cases you mentioned on the PR branch:

    • A bare wrapper is still unwrapped. test_function_response_unwraps_adk_result_wrapper checks {"result": "sunny"} → sunny and {"result": [1, 2]} → [1, 2].
    • MCP content outputs are unchanged. The content branch returns before the result check is reached, and the existing test_function_response_serializes_mcp_content_as_text passes unmodified.

    tests/unittests/integrations/openai passes (227 tests). The PR also has an end-to-end run with a real Runner showing the full dict reaching function_call_output.

    On the duplicate: #7837 was opened with this issue, about three hours before #7847 (19:05 vs. 22:09 UTC), so I'd suggest keeping #7837. Happy to go with whichever you prefer.

  4. surajksharma07 commented on Nov 3, 2026

    @surajksharma07
    Collaborator

    Thanks @adityasingh2400 for re-checking both cases. Went through #7837 and it looks good: it’s the same keys() == {'result'} rule as #7073, and since the content branch returns first, the MCP outputs aren’t affected. Testing through _content_to_response_input_items is a nice touch too, and it still merges cleanly on current main.

    Checked main and v2.13.0 again and the old 'result' in value check is still there (it’s the last place in the model connectors with that pattern), so this isn’t fixed in any release yet.

    Agreed on keeping #7837 since it came first. @ninglinLiu the change in #7847 is identical, so could you close it in favour of #7837? @xuanyang15 #7837 has 2 workflows waiting on approval whenever you get a chance.

  5. added a commit that references this issue on Nov 4, 2026
    9c45214
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

models[Component] This issue is related to model support

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions