diff --git a/core/src/main/java/com/google/adk/flows/llmflows/Contents.java b/core/src/main/java/com/google/adk/flows/llmflows/Contents.java index 91651a92d..b238842d2 100644 --- a/core/src/main/java/com/google/adk/flows/llmflows/Contents.java +++ b/core/src/main/java/com/google/adk/flows/llmflows/Contents.java @@ -434,6 +434,14 @@ private static List rearrangeEventsForAsyncFunctionResponsesInHistory( resultEvents.add(mergeFunctionResponseEvents(responseEventsToAdd)); } } + + // Skip events between the function call and the last function response. + if (!sortedIndices.isEmpty()) { + int maxIndex = sortedIndices.get(sortedIndices.size() - 1); + if (maxIndex > i) { + i = maxIndex; + } + } } } else { // gemini-3 specific part: buffer response events diff --git a/core/src/test/java/com/google/adk/flows/llmflows/ContentsTest.java b/core/src/test/java/com/google/adk/flows/llmflows/ContentsTest.java index e1756d09f..bf29e0889 100644 --- a/core/src/test/java/com/google/adk/flows/llmflows/ContentsTest.java +++ b/core/src/test/java/com/google/adk/flows/llmflows/ContentsTest.java @@ -203,9 +203,11 @@ public void rearrangeHistory_asyncFR_returnsRearrangedList() { public void rearrangeHistory_multipleFRsForSameFC_returnsMergedFR() { Event fcEvent = createFunctionCallEvent("fc1", "tool1", "call1"); Event frEvent1 = - createFunctionResponseEvent("fr1", "tool1", "call1", ImmutableMap.of("status", "running")); + createFunctionResponseEvent("fr1", "tool1", "call1", ImmutableMap.of("status", "pending")); Event frEvent2 = - createFunctionResponseEvent("fr2", "tool1", "call1", ImmutableMap.of("status", "done")); + createFunctionResponseEvent("fr2", "tool1", "call1", ImmutableMap.of("status", "running")); + Event frEvent3 = + createFunctionResponseEvent("fr3", "tool1", "call1", ImmutableMap.of("status", "done")); ImmutableList inputEvents = ImmutableList.of( createUserEvent("u1", "Query"), @@ -213,17 +215,69 @@ public void rearrangeHistory_multipleFRsForSameFC_returnsMergedFR() { createUserEvent("u2", "Wait"), frEvent1, createUserEvent("u3", "Done?"), - frEvent2); + frEvent2, + frEvent3, + createUserEvent("u4", "Follow up query")); List result = runContentsProcessor(inputEvents); - assertThat(result).hasSize(3); // u1, fc1, merged_fr + assertThat(result).hasSize(4); // u1, fc1, merged_fr, u4 assertThat(result.get(0)).isEqualTo(inputEvents.get(0).content().get()); assertThat(result.get(1)).isEqualTo(inputEvents.get(1).content().get()); // Check merged event Content mergedContent = result.get(2); assertThat(mergedContent.parts().get()).hasSize(1); assertThat(mergedContent.parts().get().get(0).functionResponse().get().response().get()) - .containsExactly("status", "done"); // Last FR wins + .containsExactly("status", "done"); // Last FR wins (frEvent3) + assertThat(result.get(3)).isEqualTo(inputEvents.get(7).content().get()); // u4 + } + + @Test + public void rearrangeHistory_multipleFRsForMultipleFC_returnsMergedFR() { + Event fcEvent1 = createFunctionCallEvent("fc1", "tool1", "call1"); + Event fcEvent2 = createFunctionCallEvent("fc2", "tool1", "call2"); + + Event frEvent1 = + createFunctionResponseEvent("fr1", "tool1", "call1", ImmutableMap.of("status", "pending")); + Event frEvent2 = + createFunctionResponseEvent("fr2", "tool1", "call1", ImmutableMap.of("status", "done")); + + Event frEvent3 = + createFunctionResponseEvent("fr3", "tool1", "call2", ImmutableMap.of("status", "pending")); + Event frEvent4 = + createFunctionResponseEvent("fr4", "tool1", "call2", ImmutableMap.of("status", "done")); + + ImmutableList inputEvents = + ImmutableList.of( + createUserEvent("u1", "I"), + fcEvent1, + createUserEvent("u2", "am"), + frEvent1, + createUserEvent("u3", "waiting"), + frEvent2, + createUserEvent("u4", "for"), + fcEvent2, + createUserEvent("u5", "you"), + frEvent3, + createUserEvent("u6", "to"), + frEvent4, + createUserEvent("u7", "Follow up query")); + + List result = runContentsProcessor(inputEvents); + + assertThat(result).hasSize(7); // u1, fc1, merged_fr, u4, fc2, merged_fr2, u7 + assertThat(result.get(0)).isEqualTo(inputEvents.get(0).content().get()); // fc1 + assertThat(result.get(1)).isEqualTo(inputEvents.get(1).content().get()); // merged_fr + Content mergedContent = result.get(2); + assertThat(mergedContent.parts().get()).hasSize(1); + assertThat(mergedContent.parts().get().get(0).functionResponse().get().response().get()) + .containsExactly("status", "done"); // Last FR wins (frEvent2) + assertThat(result.get(3)).isEqualTo(inputEvents.get(6).content().get()); // u4 + assertThat(result.get(4)).isEqualTo(inputEvents.get(7).content().get()); // fc2 + Content mergedContent2 = result.get(5); + assertThat(mergedContent2.parts().get()).hasSize(1); + assertThat(mergedContent2.parts().get().get(0).functionResponse().get().response().get()) + .containsExactly("status", "done"); // Last FR wins (merged_fr2) + assertThat(result.get(6)).isEqualTo(inputEvents.get(12).content().get()); // u7 } @Test