Skip to content

Commit c28b81e

Browse files
authored
Merge branch 'main' into tutorials-JBang
2 parents 1bc18cf + 40ec7bc commit c28b81e

File tree

11 files changed

+282
-25
lines changed

11 files changed

+282
-25
lines changed

contrib/langchain4j/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<description>LangChain4j integration for the Agent Development Kit.</description>
3131

3232
<properties>
33-
<mcp-schema.version>0.10.0</mcp-schema.version>
33+
<mcp.version>0.10.0</mcp.version>
3434
<google.genai.version>1.8.0</google.genai.version>
3535
<junit.version>5.11.4</junit.version>
3636
<mockito.version>5.17.0</mockito.version>
@@ -78,7 +78,7 @@
7878
<dependency>
7979
<groupId>io.modelcontextprotocol.sdk</groupId>
8080
<artifactId>mcp</artifactId>
81-
<version>${mcp-schema.version}</version>
81+
<version>${mcp.version}</version>
8282
</dependency>
8383

8484
<!-- Test dependencies -->

core/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<description>Agent Development Kit: an open-source, code-first toolkit designed to simplify building, evaluating, and deploying advanced AI agents anywhere.</description>
3131

3232
<properties>
33-
<mcp-schema.version>0.10.0</mcp-schema.version>
33+
<mcp.version>0.10.0</mcp.version>
3434
<errorprone.version>2.38.0</errorprone.version>
3535
<google.auth.version>1.33.1</google.auth.version>
3636
<google.cloud.storage.version>2.28.0</google.cloud.storage.version>
@@ -76,7 +76,7 @@
7676
<dependency>
7777
<groupId> io.modelcontextprotocol.sdk</groupId>
7878
<artifactId>mcp</artifactId>
79-
<version>${mcp-schema.version}</version>
79+
<version>${mcp.version}</version>
8080
</dependency>
8181
<dependency>
8282
<groupId>com.google.auth</groupId>
@@ -229,4 +229,4 @@
229229
</resource>
230230
</resources>
231231
</build>
232-
</project>
232+
</project>

core/src/main/java/com/google/adk/runner/Runner.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public BaseSessionService sessionService() {
113113
*
114114
* @throws IllegalArgumentException if message has no parts.
115115
*/
116-
private void appendNewMessageToSession(
116+
private Single<Event> appendNewMessageToSession(
117117
Session session,
118118
Content newMessage,
119119
InvocationContext invocationContext,
@@ -153,7 +153,7 @@ private void appendNewMessageToSession(
153153
.author("user")
154154
.content(Optional.of(newMessage))
155155
.build();
156-
this.sessionService.appendEvent(session, event);
156+
return this.sessionService.appendEvent(session, event);
157157
}
158158

159159
/**
@@ -217,14 +217,24 @@ public Flowable<Event> runAsync(Session session, Content newMessage, RunConfig r
217217
/* liveRequestQueue= */ Optional.empty(),
218218
runConfig);
219219

220-
if (newMessage != null) {
221-
appendNewMessageToSession(
222-
sess, newMessage, invocationContext, runConfig.saveInputBlobsAsArtifacts());
223-
}
224-
225-
invocationContext.agent(this.findAgentToRun(sess, rootAgent));
226-
Flowable<Event> events = invocationContext.agent().runAsync(invocationContext);
227-
return events.doOnNext(event -> this.sessionService.appendEvent(sess, event));
220+
Single<Event> singleEvent =
221+
(newMessage != null)
222+
? appendNewMessageToSession(
223+
sess,
224+
newMessage,
225+
invocationContext,
226+
runConfig.saveInputBlobsAsArtifacts())
227+
: Single.just(null);
228+
return singleEvent.flatMapPublisher(
229+
ignored -> {
230+
invocationContext.agent(this.findAgentToRun(sess, rootAgent));
231+
return invocationContext
232+
.agent()
233+
.runAsync(invocationContext)
234+
.flatMap(
235+
agentEvent ->
236+
this.sessionService.appendEvent(sess, agentEvent).toFlowable());
237+
});
228238
})
229239
.doOnError(
230240
throwable -> {

core/src/test/java/com/google/adk/testing/TestLlm.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131
import com.google.genai.types.Part;
3232
import io.reactivex.rxjava3.core.Completable;
3333
import io.reactivex.rxjava3.core.Flowable;
34+
import java.time.Duration;
35+
import java.time.Instant;
3436
import java.util.ArrayList;
3537
import java.util.Arrays;
3638
import java.util.Collections;
3739
import java.util.List;
3840
import java.util.NoSuchElementException;
3941
import java.util.Optional;
4042
import java.util.concurrent.atomic.AtomicInteger;
43+
import java.util.function.Predicate;
4144
import java.util.function.Supplier;
4245
import javax.annotation.Nullable;
4346

@@ -230,6 +233,36 @@ public ImmutableList<LiveRequest> getLiveRequestHistory() {
230233
return ImmutableList.copyOf(liveRequestHistory);
231234
}
232235

236+
public boolean waitForStreamingToolResults(String toolName, int expectedCount, Duration timeout) {
237+
Instant deadline = Instant.now().plus(timeout);
238+
String prefix = "Function " + toolName + " returned:";
239+
240+
Predicate<LiveRequest> isStreamingToolResult =
241+
req ->
242+
req.content()
243+
.filter(
244+
content ->
245+
content.role().orElse("").equals("user")
246+
&& content.text() != null
247+
&& content.text().startsWith(prefix))
248+
.isPresent();
249+
250+
long currentCount = 0;
251+
while (Instant.now().isBefore(deadline)) {
252+
currentCount = getLiveRequestHistory().stream().filter(isStreamingToolResult).count();
253+
if (currentCount >= expectedCount) {
254+
return true;
255+
}
256+
try {
257+
Thread.sleep(200);
258+
} catch (InterruptedException e) {
259+
Thread.currentThread().interrupt();
260+
return false;
261+
}
262+
}
263+
return false;
264+
}
265+
233266
/** A test implementation of {@link BaseLlmConnection} for {@link TestLlm}. */
234267
private final class TestLlmConnection implements BaseLlmConnection {
235268

core/src/test/java/com/google/adk/tools/streaming/StreamingToolTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.google.genai.types.FunctionResponse;
3939
import com.google.genai.types.Part;
4040
import io.reactivex.rxjava3.core.Flowable;
41+
import java.time.Duration;
4142
import java.util.ArrayList;
4243
import java.util.List;
4344
import org.junit.Test;
@@ -307,6 +308,10 @@ public void runLive_videoStreamingTool_receivesVideoFramesAndSendsResultsToLlm()
307308
RunConfig.builder().setStreamingMode(StreamingMode.BIDI).build())
308309
.toList()
309310
.blockingGet();
311+
312+
// Wait for the tool to send its 3 results back to the LLM
313+
assertThat(testLlm.waitForStreamingToolResults("monitorVideoStream", 3, Duration.ofSeconds(20)))
314+
.isTrue();
310315
// Assert that the function call was made.
311316
boolean functionCallFound =
312317
resEvents.stream()

dev/pom.xml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
<dependency>
5555
<groupId>com.google.adk</groupId>
5656
<artifactId>google-adk</artifactId>
57-
<version>0.2.1-SNAPSHOT</version><!-- {x-version-update:google-adk:current} -->
57+
<version>${project.version}</version>
5858
</dependency>
5959
<dependency>
6060
<groupId>org.springframework.boot</groupId>
@@ -69,6 +69,10 @@
6969
<artifactId>spring-boot-starter-test</artifactId>
7070
<scope>test</scope>
7171
</dependency>
72+
<dependency>
73+
<groupId>org.apache.httpcomponents.client5</groupId>
74+
<artifactId>httpclient5</artifactId>
75+
</dependency>
7276
<dependency>
7377
<groupId>guru.nidi</groupId>
7478
<artifactId>graphviz-java</artifactId>
@@ -108,7 +112,7 @@
108112
<resources>
109113
<resource>
110114
<!-- This will correctly place the contents of the 'browser' folder into 'browser/' at the
111-
root of the normal JAR, and in 'BOOT-INF/classes/browser/' in the Spring Boot repackaged
115+
root of the normal JAR, and in 'BOOT-INF/classes/browser/' in the Spring Boot repackaged
112116
executable JAR.
113117
114118
We use <directory>. and <include>browser/** instead of <directory>browser with <targetPath>browser
@@ -148,4 +152,4 @@
148152
</plugin>
149153
</plugins>
150154
</build>
151-
</project>
155+
</project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.web;
18+
19+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
20+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
21+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.params.ParameterizedTest;
25+
import org.junit.jupiter.params.provider.ValueSource;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
28+
import org.springframework.boot.test.context.SpringBootTest;
29+
import org.springframework.test.web.servlet.MockMvc;
30+
31+
/**
32+
* Integration tests for the {@link AdkWebServer} UI.
33+
*
34+
* @author <a href="http://www.vorburger.ch">Michael Vorburger.ch</a>, with Google Gemini Code
35+
* Assist in Agent mode
36+
*/
37+
@SpringBootTest
38+
@AutoConfigureMockMvc
39+
public class AdkWebServerUITest {
40+
41+
@Autowired private MockMvc mockMvc;
42+
43+
@Test
44+
public void rootShouldRedirectToDevUi() throws Exception {
45+
mockMvc
46+
.perform(get("/"))
47+
.andExpect(status().is3xxRedirection())
48+
.andExpect(redirectedUrl("/dev-ui"));
49+
}
50+
51+
@ParameterizedTest
52+
@ValueSource(strings = {"/dev-ui", "/dev-ui/"})
53+
public void devUiEndpointsShouldReturnOk(String path) throws Exception {
54+
mockMvc.perform(get(path)).andExpect(status().isOk());
55+
}
56+
57+
@Test
58+
public void nonExistentUiPageShouldReturnNotFound() throws Exception {
59+
mockMvc.perform(get("/non-existent-page")).andExpect(status().isNotFound());
60+
}
61+
}

pom.xml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<module>dev</module>
3232
<module>maven_plugin</module>
3333
<module>contrib/langchain4j</module>
34+
<module>tutorials/city-time-weather</module>
3435
</modules>
3536

3637
<properties>
@@ -181,13 +182,13 @@
181182
</plugin>
182183
<plugin>
183184
<groupId>org.sonatype.central</groupId>
184-
<artifactId>central-publishing-maven-plugin</artifactId>
185-
<version>0.8.0</version>
186-
<extensions>true</extensions>
187-
<configuration>
188-
<publishingServerId>central</publishingServerId>
189-
</configuration>
190-
</plugin>
185+
<artifactId>central-publishing-maven-plugin</artifactId>
186+
<version>0.8.0</version>
187+
<extensions>true</extensions>
188+
<configuration>
189+
<publishingServerId>central</publishingServerId>
190+
</configuration>
191+
</plugin>
191192
</plugins>
192193
</pluginManagement>
193194
<plugins>

tutorials/city-time-weather/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# City Time Weather
2+
3+
```shell
4+
GEMINI_API_KEY={YOUR-KEY} ../../mvnw exec:java
5+
```
6+
7+
See https://google.github.io/adk-docs/get-started/quickstart/#java for more information.

tutorials/city-time-weather/pom.xml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright 2025 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0"
18+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20+
<modelVersion>4.0.0</modelVersion>
21+
22+
<parent>
23+
<groupId>com.google.adk</groupId>
24+
<artifactId>google-adk-parent</artifactId>
25+
<version>0.2.1-SNAPSHOT</version><!-- {x-version-update:google-adk:current} -->
26+
</parent>
27+
28+
<artifactId>google-adk-tutorials-city-time-weather</artifactId>
29+
30+
<properties>
31+
<exec.mainClass>com.google.adk.web.AdkWebServer</exec.mainClass>
32+
</properties>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>com.google.adk</groupId>
37+
<artifactId>google-adk-dev</artifactId>
38+
<version>${project.version}</version>
39+
</dependency>
40+
</dependencies>
41+
</project>

0 commit comments

Comments
 (0)