Skip to content

Commit d0e1085

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: Update event IDs in BaseLlmFlow's post processing section
PiperOrigin-RevId: 892263598
1 parent 55becb8 commit d0e1085

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

core/src/main/java/com/google/adk/flows/llmflows/BaseLlmFlow.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,11 @@ private Flowable<Event> runOneStep(Context spanContext, InvocationContext contex
415415
})
416416
.concatMap(
417417
event -> {
418+
// Update event ID for the new resulting events
419+
String oldId = event.id();
420+
String newId = Event.generateEventId();
421+
logger.debug("Resetting event ID from {} to {}", oldId, newId);
422+
event = event.toBuilder().id(newId).build();
418423
Flowable<Event> postProcessedEvents = Flowable.just(event);
419424
if (event.actions().transferToAgent().isPresent()) {
420425
String agentToTransfer =
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.adk.flows.llmflows;
18+
19+
import static com.google.adk.testing.TestUtils.createInvocationContext;
20+
import static com.google.adk.testing.TestUtils.createLlmResponse;
21+
import static com.google.adk.testing.TestUtils.createTestAgentBuilder;
22+
import static com.google.adk.testing.TestUtils.createTestLlm;
23+
import static com.google.common.collect.ImmutableList.toImmutableList;
24+
import static com.google.common.truth.Truth.assertThat;
25+
26+
import com.google.adk.agents.InvocationContext;
27+
import com.google.adk.agents.LlmAgent;
28+
import com.google.adk.events.Event;
29+
import com.google.adk.runner.InMemoryRunner;
30+
import com.google.adk.runner.Runner;
31+
import com.google.adk.sessions.Session;
32+
import com.google.adk.tools.BaseTool;
33+
import com.google.adk.tools.ToolContext;
34+
import com.google.common.collect.ImmutableList;
35+
import com.google.common.collect.ImmutableMap;
36+
import com.google.genai.types.Content;
37+
import com.google.genai.types.FunctionDeclaration;
38+
import com.google.genai.types.Part;
39+
import com.google.genai.types.Schema;
40+
import io.reactivex.rxjava3.core.Flowable;
41+
import io.reactivex.rxjava3.core.Single;
42+
import java.util.List;
43+
import java.util.Map;
44+
import java.util.Objects;
45+
import java.util.Optional;
46+
import org.junit.Test;
47+
import org.junit.runner.RunWith;
48+
import org.junit.runners.JUnit4;
49+
50+
@RunWith(JUnit4.class)
51+
public final class ToolRequestConfirmationActionTest {
52+
53+
private static class ToolRequestConfirmationTool extends BaseTool {
54+
ToolRequestConfirmationTool() {
55+
super("request_confirmation_tool", "Requests confirmation.");
56+
}
57+
58+
@Override
59+
public Optional<FunctionDeclaration> declaration() {
60+
return Optional.of(
61+
FunctionDeclaration.builder()
62+
.name(name())
63+
.description(description())
64+
.parameters(Schema.builder().type("OBJECT").build()) // No parameters needed
65+
.build());
66+
}
67+
68+
@Override
69+
public Single<Map<String, Object>> runAsync(Map<String, Object> args, ToolContext toolContext) {
70+
toolContext.requestConfirmation("Please confirm this action");
71+
return Single.just(ImmutableMap.of());
72+
}
73+
}
74+
75+
@Test
76+
public void toolRequestConfirmation_eventHasNonNullId() {
77+
Content requestConfirmationCallContent =
78+
Content.fromParts(Part.fromFunctionCall("request_confirmation_tool", ImmutableMap.of()));
79+
Content response1 = Content.fromParts(Part.fromText("response1"));
80+
Content response2 = Content.fromParts(Part.fromText("response2"));
81+
82+
var testLlm =
83+
createTestLlm(
84+
Flowable.just(createLlmResponse(requestConfirmationCallContent)),
85+
Flowable.just(createLlmResponse(response1)),
86+
Flowable.just(createLlmResponse(response2)));
87+
88+
LlmAgent rootAgent =
89+
createTestAgentBuilder(testLlm)
90+
.name("root_agent")
91+
.tools(ImmutableList.of(new ToolRequestConfirmationTool()))
92+
.build();
93+
InvocationContext invocationContext = createInvocationContext(rootAgent);
94+
95+
Runner runner = getRunnerAndCreateSession(rootAgent, invocationContext.session());
96+
97+
ImmutableList<Event> confirmationEvents =
98+
runRunner(runner, invocationContext).stream()
99+
.filter(
100+
e ->
101+
e.functionCalls().stream()
102+
.anyMatch(
103+
f ->
104+
Objects.equals(
105+
f.name().orElse(""),
106+
Functions.REQUEST_CONFIRMATION_FUNCTION_CALL_NAME)))
107+
.collect(toImmutableList());
108+
109+
assertThat(confirmationEvents).isNotEmpty();
110+
assertThat(confirmationEvents.get(0).id()).isNotNull();
111+
}
112+
113+
private Runner getRunnerAndCreateSession(LlmAgent agent, Session session) {
114+
Runner runner = new InMemoryRunner(agent, session.appName());
115+
116+
var unused =
117+
runner
118+
.sessionService()
119+
.createSession(session.appName(), session.userId(), session.state(), session.id())
120+
.blockingGet();
121+
122+
return runner;
123+
}
124+
125+
private List<Event> runRunner(Runner runner, InvocationContext invocationContext) {
126+
Session session = invocationContext.session();
127+
return runner
128+
.runAsync(session.userId(), session.id(), invocationContext.userContent().orElse(null))
129+
.toList()
130+
.blockingGet();
131+
}
132+
}

0 commit comments

Comments
 (0)