Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,13 @@ private static String convertMapToJson(Map<String, Object> struct) {
private static boolean isEventBelongsToBranch(@Nullable String invocationBranch, Event event) {
@Nullable String eventBranch = event.branch().orElse(null);

// Branches are dot-joined agent names, so a raw prefix match would make "root.agent_10" belong
// to the branch "root.agent_1". Require either an exact match, or a prefix that ends on a
// segment boundary.
return Strings.isNullOrEmpty(invocationBranch)
|| Strings.isNullOrEmpty(eventBranch)
|| invocationBranch.startsWith(eventBranch);
|| invocationBranch.equals(eventBranch)
|| invocationBranch.startsWith(eventBranch + ".");
}

/**
Expand Down
92 changes: 92 additions & 0 deletions core/src/test/java/com/google/adk/flows/llmflows/ContentsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,61 @@ public Stream<Event> stream() {
var unused = contentsProcessor.processRequest(context, initialRequest).blockingGet();
}

@Test
public void processRequest_siblingBranchSharesNamePrefix_excludesSiblingEvent() {
Event siblingEvent =
createBranchedAgentEvent("agent_1", "e1", "sibling output", "root.agent_1");

List<Content> result =
runContentsProcessorOnBranch(ImmutableList.of(siblingEvent), "agent_10", "root.agent_10");

assertThat(result).isEmpty();
}

@Test
public void processRequest_sameBranch_includesEvent() {
Event ownEvent = createBranchedAgentEvent("agent_10", "e1", "own output", "root.agent_10");

List<Content> result =
runContentsProcessorOnBranch(ImmutableList.of(ownEvent), "agent_10", "root.agent_10");

assertThat(result).isEqualTo(eventsToContents(ImmutableList.of(ownEvent)));
}

@Test
public void processRequest_ancestorBranch_includesEvent() {
Event ancestorEvent = createBranchedAgentEvent("agent_10", "e1", "ancestor output", "root");

List<Content> result =
runContentsProcessorOnBranch(ImmutableList.of(ancestorEvent), "agent_10", "root.agent_10");

assertThat(result).isEqualTo(eventsToContents(ImmutableList.of(ancestorEvent)));
}

@Test
public void processRequest_eventWithoutBranch_includesEvent() {
Event userEvent = createUserEvent("u1", "user input");

List<Content> result =
runContentsProcessorOnBranch(ImmutableList.of(userEvent), "agent_10", "root.agent_10");

assertThat(result).isEqualTo(eventsToContents(ImmutableList.of(userEvent)));
}

@Test
public void processRequest_noInvocationBranch_includesBranchedEvent() {
Event siblingEvent =
createBranchedAgentEvent("agent_1", "e1", "sibling output", "root.agent_1");

List<Content> result =
runContentsProcessorOnBranch(ImmutableList.of(siblingEvent), "agent_10", null);

assertThat(result)
.containsExactly(
Content.fromParts(
Part.fromText("For context:"), Part.fromText("[agent_1] said: sibling output")));
}

private static Event createUserEvent(String id, String text) {
return Event.builder()
.id(id)
Expand Down Expand Up @@ -1042,6 +1097,18 @@ private static Event createAgentEvent(String agent, String id, String text) {
.build();
}

private static Event createBranchedAgentEvent(
String agent, String id, String text, String branch) {
return Event.builder()
.id(id)
.author(agent)
.content(
Content.builder().role("model").parts(ImmutableList.of(Part.fromText(text))).build())
.invocationId("invocationId")
.branch(branch)
.build();
}

private static Event createFunctionCallEvent(String id, String toolName, String callId) {
return createFunctionCallEvent(AGENT, id, toolName, callId);
}
Expand Down Expand Up @@ -1226,6 +1293,31 @@ private List<Content> runContentsProcessorGrouped(List<Event> events) {
return result.updatedRequest().contents();
}

private List<Content> runContentsProcessorOnBranch(
List<Event> events, String agentName, String invocationBranch) {
LlmAgent agent =
LlmAgent.builder()
.name(agentName)
.includeContents(LlmAgent.IncludeContents.DEFAULT)
.build();
Session session =
sessionService.createSession("test-app", "test-user", null, "test-session").blockingGet();
session.events().addAll(events);
InvocationContext context =
InvocationContext.builder()
.invocationId("test-invocation")
.agent(agent)
.session(session)
.sessionService(sessionService)
.branch(invocationBranch)
.build();

LlmRequest initialRequest = LlmRequest.builder().build();
RequestProcessor.RequestProcessingResult result =
contentsProcessor.processRequest(context, initialRequest).blockingGet();
return result.updatedRequest().contents();
}

private List<Content> runContentsProcessorWithModel(
List<Event> events, String modelName, RunConfig runConfig) {
LlmAgent agent =
Expand Down