🔴 Required Information
Describe the Bug:
When an agent backed by OCIGenAILlm has two or more tools called in
parallel in a single turn, only the result of the first tool is
forwarded to the OCI model API on the next request. All other tool results
are silently dropped.
The root cause is in _content_to_oci_message() in
src/google/adk/integrations/oci/_oci_genai_llm.py. The function correctly
collects all function_response parts from the Content into a list
called tool_results:
tool_results: list[tuple[str, str]] = [] # (tool_call_id, result_text)
for part in content.parts or []:
...
elif part.function_response:
result = part.function_response.response or {}
tool_results.append((
part.function_response.id or "",
json.dumps(result) if isinstance(result, dict) else str(result),
))
But when it builds the OCI message, it unpacks only tool_results[0] and
immediately returns a single ToolMessage, discarding every subsequent
entry (line 201):
if tool_results:
call_id, result_text = tool_results[0] # ← everything after [0] is lost
return oci_models.ToolMessage(
role=oci_models.ToolMessage.ROLE_TOOL,
tool_call_id=call_id,
content=[oci_models.TextContent(type="TEXT", text=result_text)],
)
The caller, _build_chat_details(), uses a strict one-Content-to-one-message
comprehension:
messages = [_content_to_oci_message(c) for c in llm_request.contents or []]
Because it always inserts the return value as a single element, it cannot
compensate on its own — it would also need updating to flatten a list.
This is reachable in any production agent that uses parallel tool calls, not
a hypothetical edge case: ADK's own flow layer merges individual tool-response
events into a single Content carrying multiple function_response parts
via merge_parallel_function_response_events() in
src/google/adk/flows/llm_flows/functions.py (lines 1406–1458), which runs on
both the standard and live-streaming code paths (lines 524 and 773 of the
same file).
Steps to Reproduce:
- Install
google-adk with the OCI extra (pip install google-adk[oci]).
- Construct a
Content with two function_response parts, simulating the
output of merge_parallel_function_response_events after two tools have
been called in parallel:
from google.genai import types
from google.adk.integrations.oci._oci_genai_llm import _content_to_oci_message
part_a = types.Part.from_function_response(
name="get_weather", response={"temp": "22°C"}
)
part_a.function_response.id = "call_A"
part_b = types.Part.from_function_response(
name="get_price", response={"price": "$150"}
)
part_b.function_response.id = "call_B"
content = types.Content(role="user", parts=[part_a, part_b])
result = _content_to_oci_message(content)
print(result.tool_call_id) # "call_A" — call_B's result is gone entirely
- Alternatively, run a full agent session with
OCIGenAILlm and two or
more tools where the model issues parallel tool calls, and inspect the
outgoing messages array or the resulting API error/response.
Expected Behavior:
_content_to_oci_message should return one ToolMessage per
function_response part when the Content carries more than one — exactly
as _content_to_message_param() in src/google/adk/models/lite_llm.py
already does for the equivalent LiteLLM path (returns a list of tool
messages when multiple are present; a single message otherwise). Its caller
already flattens that list-or-single-message return value into the outgoing
messages array — the OCI integration would need the same pattern in both
_content_to_oci_message and _build_chat_details.
Observed Behavior:
Only tool_results[0] is converted into a ToolMessage; every later entry
in tool_results is computed and then discarded. The final OCI messages
array contains an AssistantMessage whose tool_calls lists both
call_A and call_B, but only one matching ToolMessage (call_A). OCI
GenAI's chat API follows the OpenAI-compatible convention that every
tool_call_id from the preceding assistant turn must have a matching tool
response — a request missing one is either rejected by the backend as
malformed, or, depending on the model, answered without ever having access
to the missing tool's result.
Environment Details:
- ADK Library Version (pip show google-adk): 2.6.2 (from src/google/adk/version.py: version = "2.6.2" at commit 1a0c3bd, 2026-08-04
- Desktop OS: Windows 11
- Python Version (python -V): Python 3.11.9
Model Information:
- Are you using LiteLLM: No — this is specific to the OCI Generative AI
integration (OCIGenAILlm). LiteLLM's equivalent code path
(_content_to_message_param in lite_llm.py) already handles this
correctly, and is cited above as the precedent/reference implementation.
- Which model is being used: N/A — reproduces at the
Content-to-message
conversion level, independent of which model is configured behind OCI's
serving mode.
🟡 Optional Information
Regression:
No — the OCI integration was added this release cycle, so this isn't a
regression from previously-working behavior.
Additional Context:
- Bug location:
src/google/adk/integrations/oci/_oci_genai_llm.py,
_content_to_oci_message(), line 201.
_build_chat_details() (same file) also assumes a strict
one-Content-to-one-message mapping and would need a matching update.
- The sibling LiteLLM integration (
src/google/adk/models/lite_llm.py,
_content_to_message_param, lines 1124–1186) already solves this
correctly and can serve as the implementation reference.
- Existing unit test coverage (
tests/unittests/integrations/oci/test_oci_genai_llm.py,
test_content_to_oci_message_function_response, line 216) only covers the
single-tool-result case. No test in either the unit or integration suite
covers multiple simultaneous tool results.
- Searched open and closed issues and PRs for
_content_to_oci_message,
tool_results[0], _oci_genai_llm.py, and combinations of OCI with
parallel/function_response/tool — no existing issue or PR covers
this.
I'm working on a PR for this and will submit it shortly.
How often has this issue occurred?:
- Always (100%) — deterministic whenever a single turn involves more than
one tool call.
🔴 Required Information
Describe the Bug:
When an agent backed by
OCIGenAILlmhas two or more tools called inparallel in a single turn, only the result of the first tool is
forwarded to the OCI model API on the next request. All other tool results
are silently dropped.
The root cause is in
_content_to_oci_message()insrc/google/adk/integrations/oci/_oci_genai_llm.py. The function correctlycollects all
function_responseparts from theContentinto a listcalled
tool_results:But when it builds the OCI message, it unpacks only
tool_results[0]andimmediately returns a single
ToolMessage, discarding every subsequententry (line 201):
The caller,
_build_chat_details(), uses a strict one-Content-to-one-messagecomprehension:
Because it always inserts the return value as a single element, it cannot
compensate on its own — it would also need updating to flatten a list.
This is reachable in any production agent that uses parallel tool calls, not
a hypothetical edge case: ADK's own flow layer merges individual tool-response
events into a single
Contentcarrying multiplefunction_responsepartsvia
merge_parallel_function_response_events()insrc/google/adk/flows/llm_flows/functions.py(lines 1406–1458), which runs onboth the standard and live-streaming code paths (lines 524 and 773 of the
same file).
Steps to Reproduce:
google-adkwith the OCI extra (pip install google-adk[oci]).Contentwith twofunction_responseparts, simulating theoutput of
merge_parallel_function_response_eventsafter two tools havebeen called in parallel:
OCIGenAILlmand two ormore tools where the model issues parallel tool calls, and inspect the
outgoing
messagesarray or the resulting API error/response.Expected Behavior:
_content_to_oci_messageshould return oneToolMessageperfunction_responsepart when theContentcarries more than one — exactlyas
_content_to_message_param()insrc/google/adk/models/lite_llm.pyalready does for the equivalent LiteLLM path (returns a list of tool
messages when multiple are present; a single message otherwise). Its caller
already flattens that list-or-single-message return value into the outgoing
messagesarray — the OCI integration would need the same pattern in both_content_to_oci_messageand_build_chat_details.Observed Behavior:
Only
tool_results[0]is converted into aToolMessage; every later entryin
tool_resultsis computed and then discarded. The final OCImessagesarray contains an
AssistantMessagewhosetool_callslists bothcall_Aandcall_B, but only one matchingToolMessage(call_A). OCIGenAI's chat API follows the OpenAI-compatible convention that every
tool_call_idfrom the preceding assistant turn must have a matching toolresponse — a request missing one is either rejected by the backend as
malformed, or, depending on the model, answered without ever having access
to the missing tool's result.
Environment Details:
Model Information:
integration (
OCIGenAILlm). LiteLLM's equivalent code path(
_content_to_message_paraminlite_llm.py) already handles thiscorrectly, and is cited above as the precedent/reference implementation.
Content-to-messageconversion level, independent of which model is configured behind OCI's
serving mode.
🟡 Optional Information
Regression:
No — the OCI integration was added this release cycle, so this isn't a
regression from previously-working behavior.
Additional Context:
src/google/adk/integrations/oci/_oci_genai_llm.py,_content_to_oci_message(), line 201._build_chat_details()(same file) also assumes a strictone-
Content-to-one-message mapping and would need a matching update.src/google/adk/models/lite_llm.py,_content_to_message_param, lines 1124–1186) already solves thiscorrectly and can serve as the implementation reference.
tests/unittests/integrations/oci/test_oci_genai_llm.py,test_content_to_oci_message_function_response, line 216) only covers thesingle-tool-result case. No test in either the unit or integration suite
covers multiple simultaneous tool results.
_content_to_oci_message,tool_results[0],_oci_genai_llm.py, and combinations ofOCIwithparallel/function_response/tool— no existing issue or PR coversthis.
I'm working on a PR for this and will submit it shortly.
How often has this issue occurred?:
one tool call.