diff --git a/core/src/main/java/com/google/adk/models/GeminiLlmConnection.java b/core/src/main/java/com/google/adk/models/GeminiLlmConnection.java index 1f3d0b8c5..35dadd9fd 100644 --- a/core/src/main/java/com/google/adk/models/GeminiLlmConnection.java +++ b/core/src/main/java/com/google/adk/models/GeminiLlmConnection.java @@ -24,7 +24,6 @@ import com.google.genai.types.Blob; import com.google.genai.types.Content; import com.google.genai.types.FinishReason; -import com.google.genai.types.FunctionCall; import com.google.genai.types.FunctionResponse; import com.google.genai.types.LiveConnectConfig; import com.google.genai.types.LiveSendClientContentParameters; @@ -202,14 +201,15 @@ private static LlmResponse createToolCallResponse(LiveServerToolCall toolCall) { toolCall .functionCalls() .ifPresent( - calls -> { - for (FunctionCall call : calls) { + calls -> builder.content( Content.builder() - .parts(ImmutableList.of(Part.builder().functionCall(call).build())) - .build()); - } - }); + .role("model") + .parts( + calls.stream() + .map(call -> Part.builder().functionCall(call).build()) + .collect(toImmutableList())) + .build())); return builder.partial(false).turnComplete(false).build(); } diff --git a/core/src/test/java/com/google/adk/models/GeminiLlmConnectionTest.java b/core/src/test/java/com/google/adk/models/GeminiLlmConnectionTest.java index a3ac09fe5..b15a65852 100644 --- a/core/src/test/java/com/google/adk/models/GeminiLlmConnectionTest.java +++ b/core/src/test/java/com/google/adk/models/GeminiLlmConnectionTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.genai.types.Content; import com.google.genai.types.FunctionCall; import com.google.genai.types.GenerateContentResponseUsageMetadata; @@ -164,6 +165,7 @@ public void convertToServerResponse_withToolCall_mapsContentWithFunctionCall() { testObserver.assertComplete(); LlmResponse response = testObserver.values().get(0); assertThat(response.content()).isPresent(); + assertThat(response.content().get().role()).hasValue("model"); assertThat(response.content().get().parts()).isPresent(); assertThat(response.content().get().parts().get()).hasSize(1); assertThat(response.content().get().parts().get().get(0).functionCall()).hasValue(functionCall); @@ -171,6 +173,39 @@ public void convertToServerResponse_withToolCall_mapsContentWithFunctionCall() { assertThat(response.turnComplete()).hasValue(false); } + @Test + public void convertToServerResponse_withMultipleFunctionCalls_preservesAllCallsAsParts() { + // Regression test for parallel tool calling on the live/BIDI path: a single toolCall message + // can carry multiple FunctionCalls, and all of them must be preserved (not just the last). + FunctionCall getWeather = + FunctionCall.builder().name("getWeather").args(ImmutableMap.of("city", "Paris")).build(); + FunctionCall getTime = + FunctionCall.builder() + .name("getTime") + .args(ImmutableMap.of("timezone", "Europe/London")) + .build(); + LiveServerToolCall toolCall = + LiveServerToolCall.builder().functionCalls(ImmutableList.of(getWeather, getTime)).build(); + + LiveServerMessage message = LiveServerMessage.builder().toolCall(toolCall).build(); + + TestObserver testObserver = new TestObserver<>(); + + GeminiLlmConnection.convertToServerResponse(message).subscribe(testObserver); + + testObserver.assertValueCount(1); + testObserver.assertComplete(); + LlmResponse response = testObserver.values().get(0); + assertThat(response.content()).isPresent(); + assertThat(response.content().get().role()).hasValue("model"); + assertThat(response.content().get().parts()).isPresent(); + assertThat(response.content().get().parts().get()).hasSize(2); + assertThat(response.content().get().parts().get().get(0).functionCall()).hasValue(getWeather); + assertThat(response.content().get().parts().get().get(1).functionCall()).hasValue(getTime); + assertThat(response.partial()).hasValue(false); + assertThat(response.turnComplete()).hasValue(false); + } + @Test public void convertToServerResponse_withUsageMetadata_mapsGenerateResponseUsageMetadata() { LiveServerMessage message =