Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion core/src/main/java/com/google/adk/flows/llmflows/Contents.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ public Single<RequestProcessor.RequestProcessingResult> processRequest(
if (llmAgent.includeContents() == LlmAgent.IncludeContents.NONE) {
return Single.just(
RequestProcessor.RequestProcessingResult.create(
request.toBuilder().contents(ImmutableList.of()).build(), ImmutableList.of()));
request.toBuilder()
.contents(
getCurrentTurnContents(
context.branch(), context.session().events(), context.agent().name()))
.build(),
ImmutableList.of()));
}

ImmutableList<Content> contents =
Expand All @@ -67,6 +72,19 @@ public Single<RequestProcessor.RequestProcessingResult> processRequest(
request.toBuilder().contents(contents).build(), ImmutableList.of()));
}

/** Gets contents for the current turn only (no conversation history). */
private ImmutableList<Content> getCurrentTurnContents(
Optional<String> currentBranch, List<Event> events, String agentName) {
// Find the latest event that starts the current turn and process from there.
for (int i = events.size() - 1; i >= 0; i--) {
Event event = events.get(i);
if (event.author().equals("user") || isOtherAgentReply(agentName, event)) {
return getContents(currentBranch, events.subList(i, events.size()), agentName);
}
}
return ImmutableList.of();
}

private ImmutableList<Content> getContents(
Optional<String> currentBranch, List<Event> events, String agentName) {
List<Event> filteredEvents = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,84 @@ public void convertForeignEvent_eventsFromOtherAgents_returnsContextualOnlyEvent
.inOrder();
}

@Test
public void processRequest_includeContentsNone_lastEventIsUser() {
ImmutableList<Event> events =
ImmutableList.of(
createUserEvent("u1", "Turn 1"),
createAgentEvent("a1", "Reply 1"),
createUserEvent("u2", "Turn 2"));
List<Content> result =
runContentsProcessorWithIncludeContents(events, LlmAgent.IncludeContents.NONE);
assertThat(result).containsExactly(events.get(2).content().get());
}

@Test
public void processRequest_includeContentsNone_lastEventIsOtherAgent() {
ImmutableList<Event> events =
ImmutableList.of(
createUserEvent("u1", "Turn 1"),
createAgentEvent("a1", "Reply 1"),
createAgentEvent(OTHER_AGENT, "oa1", "Other Agent Turn"));
List<Content> result =
runContentsProcessorWithIncludeContents(events, LlmAgent.IncludeContents.NONE);
assertThat(result)
.containsExactly(
Content.fromParts(
Part.fromText("For context:"),
Part.fromText("[other_agent] said: Other Agent Turn")));
}

@Test
public void processRequest_includeContentsNone_noUserMessage() {
ImmutableList<Event> events =
ImmutableList.of(
createAgentEvent("a1", "Reply 1"),
createFunctionCallEvent("fc1", "tool1", "call1"),
createFunctionResponseEvent("fr1", "tool1", "call1"));
List<Content> result =
runContentsProcessorWithIncludeContents(events, LlmAgent.IncludeContents.NONE);
assertThat(result).isEmpty();
}

@Test
public void processRequest_includeContentsNone_asyncFRAcrossTurns_throwsException() {
Event u1 = createUserEvent("u1", "Query 1");
Event fc1 = createFunctionCallEvent("fc1", "tool1", "call1");
Event u2 = createUserEvent("u2", "Query 2");
Event fr1 = createFunctionResponseEvent("fr1", "tool1", "call1"); // FR for fc1

ImmutableList<Event> events = ImmutableList.of(u1, fc1, u2, fr1);

// The current turn starts from u2. fc1 is not in the sublist [u2, fr1], so rearrangement fails.
IllegalStateException e =
assertThrows(
IllegalStateException.class,
() -> runContentsProcessorWithIncludeContents(events, LlmAgent.IncludeContents.NONE));
assertThat(e)
.hasMessageThat()
.contains("No function call event found for function response IDs: [call1]");
}

@Test
public void processRequest_includeContentsNone_asyncFRWithinTurn() {
Event u1 = createUserEvent("u1", "Query 1");
Event fc1 = createFunctionCallEvent("fc1", "tool1", "call1");
Event a1 = createAgentEvent("a1", "Agent thinking");
Event fr1 = createFunctionResponseEvent("fr1", "tool1", "call1");

ImmutableList<Event> events = ImmutableList.of(u1, fc1, a1, fr1);
// Current turn starts with u1. The list passed to getContents is [u1, fc1, a1, fr1].
List<Content> result =
runContentsProcessorWithIncludeContents(events, LlmAgent.IncludeContents.NONE);
assertThat(result)
.containsExactly(
events.get(0).content().get(), // u1
events.get(1).content().get(), // fc1
events.get(3).content().get()) // fr1 (merged)
.inOrder();
}

private static Event createUserEvent(String id, String text) {
return Event.builder()
.id(id)
Expand Down Expand Up @@ -503,7 +581,12 @@ private static Event createFunctionResponseEvent(
}

private List<Content> runContentsProcessor(List<Event> events) {
LlmAgent agent = LlmAgent.builder().name(AGENT).build();
return runContentsProcessorWithIncludeContents(events, LlmAgent.IncludeContents.DEFAULT);
}

private List<Content> runContentsProcessorWithIncludeContents(
List<Event> events, LlmAgent.IncludeContents includeContents) {
LlmAgent agent = LlmAgent.builder().name(AGENT).includeContents(includeContents).build();
Session session =
Session.builder("test-session")
.appName("test-app")
Expand Down