Skip to content

[gemini] Fix parallel function calls in HLI tool loop - #21335

Merged
lsiepel merged 2 commits into
openhab:mainfrom
cheldt:fix-gemini-parallel-function-calls
Aug 14, 2026
Merged

[gemini] Fix parallel function calls in HLI tool loop#21335
lsiepel merged 2 commits into
openhab:mainfrom
cheldt:fix-gemini-parallel-function-calls

Conversation

@cheldt

@cheldt cheldt commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fix parallel function calls in HLI tool loop

Gemini can return several functionCall parts in one model turn, with the thought signature only on the first part. The HLI stored each call as its own TOOL_CALL/TOOL_RETURN pair and the API client replayed them as separate single-call model turns, which Gemini 3.x rejects with 400 INVALID_ARGUMENT (missing thought_signature).

Batch parallel calls into a single TOOL_CALL message (JSON array) and their results into a single TOOL_RETURN (JSON array of strings), which also satisfies the conversation rule that a TOOL_RETURN must directly follow its TOOL_CALL. The API client rebuilds them as one model turn with all functionCall parts and one user turn with all functionResponse parts, now echoing the per-call id so equal-named parallel calls pair unambiguously.

Includes unit tests for the JSON batch (de-)serialization helpers.

Fixes #21333

@cheldt
cheldt requested a review from florian-h05 as a code owner August 7, 2026 10:29
@cheldt
cheldt force-pushed the fix-gemini-parallel-function-calls branch from 23b1d5c to e5d0bd9 Compare August 7, 2026 10:37
@wborn
wborn requested a balanced review from Copilot August 7, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Gemini parallel tool-call replay by preserving calls and responses as complete model/user turns.

Changes:

  • Batches parallel tool calls and results in conversation history.
  • Reconstructs matching function calls/responses with per-call IDs.
  • Adds batch serialization and validation tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
GeminiHLIService.java Batches parallel calls and results.
GeminiApiClient.java Rebuilds batched Gemini turns.
GeminiLLMToolCall.java Adds batch JSON helpers.
GeminiFunctionResponse.java Adds response call IDs.
GeminiLLMToolCallTest.java Tests JSON formats and validation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@wborn wborn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review performed before manual maintainer review.

The parallel function-call handling looks correct. The calls are now kept together as one model turn, the corresponding responses are grouped into the following user turn, and the per-call IDs and thought signature are preserved as required by the Gemini API.

AI found no correctness issues in the implementation. There is one non-blocking test-coverage suggestion inline: it would be useful to cover the reconstructed Gemini request shape in addition to the JSON helper serialization tests.

A human maintainer review is still needed.

@cheldt

cheldt commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

@wborn Addressed review

@cheldt
cheldt force-pushed the fix-gemini-parallel-function-calls branch from c3863e0 to 4e8ee71 Compare August 10, 2026 16:06

@wborn wborn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requested regression coverage has been added and verifies the actual Gemini request reconstruction for parallel function calls, including the grouped model/user turns, thought signature, and function call IDs.

AI found no further issues.

A human maintainer review is still needed.

@lsiepel lsiepel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM

Awaiting confirmation by @florian-h05 as codeowner

@florian-h05

Copy link
Copy Markdown
Contributor

I’ll have a look in the next days, currently on vacation but will be back home tomorrow evening.

@florian-h05 florian-h05 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM, however there is a serious violation of the HumanLanguageInterpreter API contract:

cheldt added 2 commits August 14, 2026 11:35
Gemini can return several functionCall parts in one model turn, with
the thought signature only on the first part. The HLI stored each call
as its own TOOL_CALL/TOOL_RETURN pair and the API client replayed them
as separate single-call model turns, which Gemini 3.x rejects with 400
INVALID_ARGUMENT (missing thought_signature).

Keep storing each call as its own TOOL_CALL/TOOL_RETURN message pair -
preserving the message format defined by the HumanLanguageInterpreter
API contract (consumed e.g. by Main UI) and the Conversation rule that
a tool result must directly follow its tool call - but mark calls 2..N
of a parallel batch with a binding-private "parallel":true field in the
stored GeminiLLMToolCall JSON, in the same way as the existing
id/thoughtSignature extension fields. The field is omitted when null,
so single calls keep the exact previous message format and legacy
conversations still replay.

The API client uses the marker to rebuild a batch as one model turn
with all functionCall parts and one user turn with all functionResponse
parts, now echoing the per-call id so equal-named parallel calls pair
unambiguously.

Includes unit tests for the (de-)serialization of the parallel marker.

Signed-off-by: Christian Heldt <snaut@tutanota.com>
The parallel marker (de-)serialization is covered by unit tests, but
the request shape that actually fixes the parallel call rejection was
not. Add a test that drives sendPrompt() with an interleaved two-call
history whose second call is marked parallel, captures the serialized
request body and asserts that it contains one model content with both
functionCall parts followed by one user content with both matching
functionResponse parts, preserving the first call's thoughtSignature
and both call ids.

A second test covers the legacy single-call message format to make sure
conversations stored by earlier versions still replay and that absent
call ids are omitted from the request. A third test ensures sequential
(unmarked) tool calls are not merged and keep their own model turns.

Signed-off-by: Christian Heldt <snaut@tutanota.com>
@cheldt
cheldt force-pushed the fix-gemini-parallel-function-calls branch from 4e8ee71 to 131c6a8 Compare August 14, 2026 09:46
@lsiepel
lsiepel requested a review from florian-h05 August 14, 2026 10:00
@florian-h05

Copy link
Copy Markdown
Contributor

@cheldt Please never force push during review, this resets review progress. In this case the diff luckily isn't that large, just don't do it in the future. Thanks!

@florian-h05 florian-h05 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@lsiepel lsiepel added the bug An unexpected problem or unintended behavior of an add-on label Aug 14, 2026
@lsiepel
lsiepel merged commit fd2204d into openhab:main Aug 14, 2026
3 checks passed
@lsiepel lsiepel added this to the 5.3 milestone Aug 14, 2026
cipianpascu pushed a commit to cipianpascu/openhab-addons that referenced this pull request Aug 16, 2026
* [gemini] Fix parallel function calls in HLI tool loop

Signed-off-by: Christian Heldt <snaut@tutanota.com>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug An unexpected problem or unintended behavior of an add-on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[gemini] HLI fails with 400 INVALID_ARGUMENT when Gemini makes parallel function calls

5 participants