Skip to content

Commit dcd08a4

Browse files
MarkDaoustcopybara-github
authored andcommitted
feat: add support for audio, video, text and session resumption in java.
PiperOrigin-RevId: 756447668
1 parent 86b0553 commit dcd08a4

File tree

12 files changed

+777
-15
lines changed

12 files changed

+777
-15
lines changed

examples/src/main/java/com/google/genai/examples/LiveTextConversationAsync.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ public static void main(String[] args) {
6565
LiveConnectConfig.builder()
6666
.responseModalitiesFromKnown(ImmutableList.of(Modality.Known.TEXT))
6767
.topP(0.8f)
68-
.maxOutputTokens(30)
6968
.seed(1234)
7069
.build();
7170

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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+
* https://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+
/**
18+
* Usage:
19+
*
20+
* <p>1a. If you are using Vertex AI, setup ADC to get credentials:
21+
* https://cloud.google.com/docs/authentication/provide-credentials-adc#google-idp
22+
*
23+
* <p>Then set Project, Location, and USE_VERTEXAI flag as environment variables:
24+
*
25+
* <p>export GOOGLE_CLOUD_PROJECT=YOUR_PROJECT
26+
*
27+
* <p>export GOOGLE_CLOUD_LOCATION=YOUR_LOCATION
28+
*
29+
* <p>1b. If you are using Gemini Developer AI, set an API key environment variable. You can find a
30+
* list of available API keys here: https://aistudio.google.com/app/apikey
31+
*
32+
* <p>export GOOGLE_API_KEY=YOUR_API_KEY
33+
*
34+
* <p>2. Compile the java package and run the sample code.
35+
*
36+
* <p>mvn clean compile exec:java
37+
* -Dexec.mainClass="com.google.genai.examples.LiveTextConversationAsync"
38+
*
39+
* <p>to resume a session, you can use the --session_handle argument to provide the session handle
40+
* returned in the session resumption update from the server.
41+
*
42+
* <p>mvn clean compile exec:java
43+
* -Dexec.mainClass="com.google.genai.examples.LiveTextConversationResumptionAsync"
44+
* -Dexec.args="--session_handle=..."
45+
*/
46+
package com.google.genai.examples;
47+
48+
import com.google.common.collect.ImmutableList;
49+
import com.google.genai.AsyncSession;
50+
import com.google.genai.Client;
51+
import com.google.genai.types.Content;
52+
import com.google.genai.types.HttpOptions;
53+
import com.google.genai.types.LiveConnectConfig;
54+
import com.google.genai.types.LiveSendRealtimeInputParameters;
55+
import com.google.genai.types.LiveServerContent;
56+
import com.google.genai.types.LiveServerMessage;
57+
import com.google.genai.types.LiveServerSessionResumptionUpdate;
58+
import com.google.genai.types.Modality;
59+
import com.google.genai.types.Part;
60+
import com.google.genai.types.SessionResumptionConfig;
61+
import java.io.Console;
62+
import java.util.concurrent.CompletableFuture;
63+
import java.util.concurrent.ExecutionException;
64+
65+
/** Example of using the live module to send and receive messages asynchronously. */
66+
public class LiveTextConversationResumptionAsync {
67+
68+
public static void main(String[] args) {
69+
// Get the session handle from the command line, if provided
70+
String sessionHandle = null;
71+
if (args.length > 0) {
72+
if (args[0].startsWith("--session_handle")) {
73+
String[] parts = args[0].split("=", 2);
74+
if (parts.length == 2) {
75+
sessionHandle = parts[1];
76+
} else if (parts.length == 1) {
77+
System.err.println("Error: --session_handle requires a value.");
78+
System.err.println("Usage: mvn ... --session_handle=<your_handle_value>");
79+
System.exit(1);
80+
}
81+
}
82+
}
83+
84+
// Instantiates the client.
85+
Client client =
86+
Client.builder().httpOptions(HttpOptions.builder().apiVersion("v1beta").build()).build();
87+
88+
SessionResumptionConfig.Builder sessionResumptionConfigBuilder =
89+
SessionResumptionConfig.builder();
90+
if (sessionHandle != null) {
91+
System.out.println("Resuming session handle: " + sessionHandle);
92+
sessionResumptionConfigBuilder.handle(sessionHandle);
93+
}
94+
SessionResumptionConfig sessionResumptionConfig = sessionResumptionConfigBuilder.build();
95+
96+
LiveConnectConfig config =
97+
LiveConnectConfig.builder()
98+
.responseModalitiesFromKnown(ImmutableList.of(Modality.Known.TEXT))
99+
.sessionResumption(sessionResumptionConfig)
100+
.topP(0.8f)
101+
.seed(1234)
102+
.build();
103+
104+
CompletableFuture<Void> allDone = new CompletableFuture<>();
105+
106+
AsyncSession session;
107+
108+
try {
109+
if (client.vertexAI()) {
110+
System.out.println("vertex");
111+
session = client.async.live.connect("gemini-2.0-flash-live-preview-04-09", config).get();
112+
} else {
113+
System.out.println("mldev");
114+
session = client.async.live.connect("gemini-2.0-flash-live-001", config).get();
115+
}
116+
// Start receiving messages.
117+
CompletableFuture<Void> receiveFuture =
118+
session.receive(message -> printLiveServerMessage(message, allDone));
119+
120+
// Send messages. Keep sending until user quits.
121+
System.out.print("Your Turn >> ");
122+
boolean keepSending = true;
123+
while (keepSending) {
124+
keepSending = send(session).get();
125+
}
126+
127+
allDone.get();
128+
receiveFuture.get();
129+
session.close().get();
130+
System.out.println("Session closed.");
131+
132+
} catch (InterruptedException | ExecutionException e) {
133+
System.err.println("An error occurred: " + e.getMessage());
134+
e.printStackTrace();
135+
}
136+
}
137+
138+
public static void printLiveServerMessage(
139+
LiveServerMessage message, CompletableFuture<Void> allDone) {
140+
if (message.serverContent().isPresent()) {
141+
printServerContent(message.serverContent().get(), allDone);
142+
} else if (message.sessionResumptionUpdate().isPresent()) {
143+
printSessionResumptionUpdate(message.sessionResumptionUpdate().get());
144+
}
145+
}
146+
147+
public static LiveSendClientContentParameters clientContentFromText(String text) {
148+
return LiveSendClientContentParameters.builder()
149+
.turnComplete(true)
150+
.turns(
151+
ImmutableList.of(
152+
Content.builder()
153+
.parts(ImmutableList.of(Part.builder().text(text).build()))
154+
.build()))
155+
.build();
156+
}
157+
158+
private static void printServerContent(
159+
LiveServerContent content, CompletableFuture<Void> allDone) {
160+
content
161+
.modelTurn()
162+
.ifPresent(
163+
modelTurn ->
164+
modelTurn
165+
.parts()
166+
.ifPresent(
167+
parts -> parts.forEach(part -> part.text().ifPresent(System.out::print))));
168+
169+
if (content.turnComplete().orElse(false)) {
170+
System.out.println("Your Turn >> ");
171+
allDone.complete(null);
172+
}
173+
}
174+
175+
private static void printSessionResumptionUpdate(LiveServerSessionResumptionUpdate update) {
176+
System.out.println("\n\n<< Session Resumption Update: ");
177+
if (update.newHandle().isPresent()) {
178+
System.out.println("<< New Handle: " + update.newHandle().get() + ", ");
179+
}
180+
if (update.resumable().isPresent()) {
181+
System.out.println("<< Resumable: " + update.resumable().get() + ", ");
182+
}
183+
if (update.lastConsumedClientMessageIndex().isPresent()) {
184+
System.out.println(
185+
"<< Last Consumed Index: " + update.lastConsumedClientMessageIndex().get());
186+
}
187+
System.out.println();
188+
}
189+
190+
private static CompletableFuture<Boolean> send(AsyncSession session) {
191+
Console console = System.console();
192+
if (console == null) {
193+
System.err.println("No console available.");
194+
return CompletableFuture.completedFuture(false);
195+
}
196+
try {
197+
String textInput = console.readLine();
198+
if (textInput == null || textInput.toLowerCase().matches("q|quit|exit")) {
199+
return CompletableFuture.completedFuture(false);
200+
}
201+
return session.sendClientContent(clientContentFromText(textInput)).thenApply(unused -> true);
202+
203+
} catch (Exception e) {
204+
return CompletableFuture.completedFuture(false);
205+
}
206+
}
207+
}

src/main/java/com/google/genai/AsyncSession.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,8 @@
1818

1919
import com.fasterxml.jackson.databind.JsonNode;
2020
import com.fasterxml.jackson.databind.node.ObjectNode;
21-
import com.google.common.collect.ImmutableList;
22-
import com.google.genai.types.Blob;
2321
import com.google.genai.types.LiveClientContent;
2422
import com.google.genai.types.LiveClientMessage;
25-
import com.google.genai.types.LiveClientRealtimeInput;
2623
import com.google.genai.types.LiveClientToolResponse;
2724
import com.google.genai.types.LiveSendClientContentParameters;
2825
import com.google.genai.types.LiveSendRealtimeInputParameters;
@@ -70,14 +67,9 @@ public CompletableFuture<Void> sendClientContent(LiveSendClientContentParameters
7067
* future will fail if the realtime input cannot be sent.
7168
*/
7269
public CompletableFuture<Void> sendRealtimeInput(LiveSendRealtimeInputParameters realtimeInput) {
73-
return send(
74-
LiveClientMessage.builder()
75-
.realtimeInput(
76-
LiveClientRealtimeInput.builder()
77-
.mediaChunks(
78-
ImmutableList.of(realtimeInput.media().orElse(Blob.builder().build())))
79-
.build())
80-
.build());
70+
LiveClientMessage msg =
71+
LiveClientMessage.builder().realtimeInputParameters(realtimeInput).build();
72+
return send(msg);
8173
}
8274

8375
/**

0 commit comments

Comments
 (0)