diff --git a/genai/snippets/pom.xml b/genai/snippets/pom.xml index 3c59e1fde38..a27c899bc68 100644 --- a/genai/snippets/pom.xml +++ b/genai/snippets/pom.xml @@ -51,17 +51,13 @@ com.google.genai google-genai - 1.23.0 + 1.28.0 com.google.cloud google-cloud-storage test - - com.google.cloud - google-cloud-storage - junit junit diff --git a/genai/snippets/src/main/java/genai/live/LiveCodeExecWithTxt.java b/genai/snippets/src/main/java/genai/live/LiveCodeExecWithTxt.java new file mode 100644 index 00000000000..a75b829155f --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveCodeExecWithTxt.java @@ -0,0 +1,120 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package genai.live; + +// [START googlegenaisdk_live_code_exec_with_txt] + +import static com.google.genai.types.Modality.Known.TEXT; + +import com.google.genai.AsyncSession; +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.LiveConnectConfig; +import com.google.genai.types.LiveSendClientContentParameters; +import com.google.genai.types.LiveServerContent; +import com.google.genai.types.LiveServerMessage; +import com.google.genai.types.Part; +import com.google.genai.types.Tool; +import com.google.genai.types.ToolCodeExecution; +import java.util.concurrent.CompletableFuture; + +public class LiveCodeExecWithTxt { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash-live-preview-04-09"; + generateContent(modelId); + } + + // Shows how to generate content with the Code Execution tool and a text input. + public static String generateContent(String modelId) { + // Client Initialization. Once created, it can be reused for multiple requests. + try (Client client = Client.builder().location("us-central1").vertexAI(true).build()) { + + // Connects to the live server. + CompletableFuture sessionFuture = + client.async.live.connect( + modelId, + LiveConnectConfig.builder() + .responseModalities(TEXT) + .tools(Tool.builder().codeExecution(ToolCodeExecution.builder().build()).build()) + .build()); + + // Sends and receives messages from the live session. + CompletableFuture responseFuture = + sessionFuture.thenCompose( + session -> { + // A future that completes when the model signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // A variable to concatenate the text response from the model. + StringBuilder modelResponse = new StringBuilder(); + // Starts receiving messages from the live session. + session.receive( + message -> handleLiveServerMessage(message, turnComplete, modelResponse)); + // Sends content to the live session and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose( + unused -> session.close().thenApply(result -> modelResponse.toString())); + }); + + String response = responseFuture.join(); + System.out.println(response); + // Example output: + // > Compute the largest prime palindrome under 100000 + // + // Okay, I understand. I need to find the largest prime number that is also a palindrome + // and is less than 100000. Here's my plan... + return response; + } + } + + // Sends content to the live session. + private static CompletableFuture sendContent(AsyncSession session) { + String textInput = "Compute the largest prime palindrome under 100000"; + System.out.printf("> %s\n", textInput); + return session.sendClientContent( + LiveSendClientContentParameters.builder() + .turns(Content.builder().role("user").parts(Part.fromText(textInput)).build()) + .turnComplete(true) + .build()); + } + + // Concatenates the response messages from the model and signals + // `turnComplete` when the model is done generating the response. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete, StringBuilder response) { + message + .serverContent() + .flatMap(LiveServerContent::modelTurn) + .flatMap(Content::parts) + .ifPresent( + parts -> + parts.forEach( + part -> { + part.text().ifPresent(response::append); + part.executableCode().ifPresent(code -> System.out.println("code: " + code)); + part.codeExecutionResult() + .ifPresent(result -> System.out.println("result: " + result)); + })); + // Checks if the model's turn is over. + if (message.serverContent().flatMap(LiveServerContent::turnComplete).orElse(false)) { + turnComplete.complete(null); + } + } +} +// [END googlegenaisdk_live_code_exec_with_txt] diff --git a/genai/snippets/src/main/java/genai/live/LiveGroundGoogSearchWithTxt.java b/genai/snippets/src/main/java/genai/live/LiveGroundGoogSearchWithTxt.java new file mode 100644 index 00000000000..2807f09a4d1 --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveGroundGoogSearchWithTxt.java @@ -0,0 +1,112 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package genai.live; + +// [START googlegenaisdk_live_ground_googsearch_with_txt] + +import static com.google.genai.types.Modality.Known.TEXT; + +import com.google.genai.AsyncSession; +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.GoogleSearch; +import com.google.genai.types.LiveConnectConfig; +import com.google.genai.types.LiveSendClientContentParameters; +import com.google.genai.types.LiveServerContent; +import com.google.genai.types.LiveServerMessage; +import com.google.genai.types.Part; +import com.google.genai.types.Tool; +import java.util.concurrent.CompletableFuture; + +public class LiveGroundGoogSearchWithTxt { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash-live-preview-04-09"; + generateContent(modelId); + } + + // Shows how to generate content with the Google Search tool and a text input. + public static String generateContent(String modelId) { + // Client Initialization. Once created, it can be reused for multiple requests. + try (Client client = Client.builder().location("us-central1").vertexAI(true).build()) { + + // Connects to the live server. + CompletableFuture sessionFuture = + client.async.live.connect( + modelId, + LiveConnectConfig.builder() + .responseModalities(TEXT) + .tools(Tool.builder().googleSearch(GoogleSearch.builder().build()).build()) + .build()); + + // Sends and receives messages from the live session. + CompletableFuture responseFuture = + sessionFuture.thenCompose( + session -> { + // A future that completes when the model signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // A variable to concatenate the text response from the model. + StringBuilder modelResponse = new StringBuilder(); + // Starts receiving messages from the live session. + session.receive( + message -> handleLiveServerMessage(message, turnComplete, modelResponse)); + // Sends content to the live session and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose( + unused -> session.close().thenApply(result -> modelResponse.toString())); + }); + + String response = responseFuture.join(); + System.out.println(response); + // Example output: + // > When did the last Brazil vs. Argentina soccer match happen? + // + // The most recent Brazil vs. Argentina soccer match was on March 25, 2025, + // as part of the 2026 World Cup qualifiers. Argentina won 4-1. + return response; + } + } + + // Sends content to the live session. + private static CompletableFuture sendContent(AsyncSession session) { + String textInput = "When did the last Brazil vs. Argentina soccer match happen?"; + System.out.printf("> %s\n", textInput); + return session.sendClientContent( + LiveSendClientContentParameters.builder() + .turns(Content.builder().role("user").parts(Part.fromText(textInput)).build()) + .turnComplete(true) + .build()); + } + + // Concatenates the response messages from the model and signals + // `turnComplete` when the model is done generating the response. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete, StringBuilder response) { + message + .serverContent() + .flatMap(LiveServerContent::modelTurn) + .flatMap(Content::parts) + .ifPresent(parts -> parts.forEach(part -> part.text().ifPresent(response::append))); + // Checks if the model's turn is over. + if (message.serverContent().flatMap(LiveServerContent::turnComplete).orElse(false)) { + turnComplete.complete(null); + } + } +} +// [END googlegenaisdk_live_ground_googsearch_with_txt] diff --git a/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java b/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java new file mode 100644 index 00000000000..14ac56f2f82 --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java @@ -0,0 +1,126 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package genai.live; + +// [START googlegenaisdk_live_transcribe_with_audio] + +import static com.google.genai.types.Modality.Known.AUDIO; + +import com.google.genai.AsyncSession; +import com.google.genai.Client; +import com.google.genai.types.AudioTranscriptionConfig; +import com.google.genai.types.Content; +import com.google.genai.types.LiveConnectConfig; +import com.google.genai.types.LiveSendClientContentParameters; +import com.google.genai.types.LiveServerContent; +import com.google.genai.types.LiveServerMessage; +import com.google.genai.types.Part; +import com.google.genai.types.Transcription; +import java.util.concurrent.CompletableFuture; + +public class LiveTranscribeWithAudio { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-live-2.5-flash-preview-native-audio"; + generateContent(modelId); + } + + // Shows how to transcribe audio. + public static String generateContent(String modelId) { + // Client Initialization. Once created, it can be reused for multiple requests. + try (Client client = Client.builder().location("us-central1").vertexAI(true).build()) { + + // Connects to the live server. + CompletableFuture sessionFuture = + client.async.live.connect( + modelId, + LiveConnectConfig.builder() + .responseModalities(AUDIO) + .inputAudioTranscription(AudioTranscriptionConfig.builder().build()) + .outputAudioTranscription(AudioTranscriptionConfig.builder().build()) + .build()); + + // Sends and receives messages from the live session. + CompletableFuture responseFuture = + sessionFuture.thenCompose( + session -> { + // A future that completes when the model signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // A variable to concatenate the text response from the model. + StringBuilder modelResponse = new StringBuilder(); + // Starts receiving messages from the live session. + session.receive( + message -> handleLiveServerMessage(message, turnComplete, modelResponse)); + // Sends content to the live session and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose( + unused -> session.close().thenApply(result -> modelResponse.toString())); + }); + + String response = responseFuture.join(); + System.out.println(response); + // Example output: + // > Hello? Gemini, are you there? + // + // Yes, I'm here. How can I help you today? + return response; + } + } + + // Sends content to the live session. + private static CompletableFuture sendContent(AsyncSession session) { + String textInput = "Hello? Gemini, are you there?"; + System.out.printf("> %s\n", textInput); + return session.sendClientContent( + LiveSendClientContentParameters.builder() + .turns(Content.builder().role("user").parts(Part.fromText(textInput)).build()) + .turnComplete(true) + .build()); + } + + // Concatenates the output transcription from the model and signals + // `turnComplete` when the model is done generating the response. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete, StringBuilder response) { + + message + .serverContent() + .ifPresent( + serverContent -> { + serverContent + .modelTurn() + .ifPresent(modelTurn -> System.out.println("Model turn: " + modelTurn.parts())); + + serverContent + .inputTranscription() + .flatMap(Transcription::text) + .ifPresent(text -> System.out.println("Input transcript: " + text)); + + serverContent + .outputTranscription() + .flatMap(Transcription::text) + .ifPresent(response::append); + // Checks if the model's turn is over. + if (serverContent.turnComplete().orElse(false)) { + turnComplete.complete(null); + } + }); + } +} +// [END googlegenaisdk_live_transcribe_with_audio] diff --git a/genai/snippets/src/main/java/genai/live/LiveWithTxt.java b/genai/snippets/src/main/java/genai/live/LiveWithTxt.java new file mode 100644 index 00000000000..3607b92f67e --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveWithTxt.java @@ -0,0 +1,111 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package genai.live; + +// [START googlegenaisdk_live_with_txt] + +import static com.google.genai.types.Modality.Known.TEXT; + +import com.google.genai.AsyncSession; +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.LiveConnectConfig; +import com.google.genai.types.LiveSendClientContentParameters; +import com.google.genai.types.LiveServerContent; +import com.google.genai.types.LiveServerMessage; +import com.google.genai.types.Part; +import java.util.concurrent.CompletableFuture; + +public class LiveWithTxt { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash-live-preview-04-09"; + generateContent(modelId); + } + + // Shows how to send a text prompt and receive messages from the live session. + public static String generateContent(String modelId) { + // Client Initialization. Once created, it can be reused for multiple requests. + try (Client client = + Client.builder() + .location("us-central1") + .vertexAI(true) + .httpOptions(HttpOptions.builder().apiVersion("v1beta1").build()) + .build()) { + + // Connects to the live server. + CompletableFuture sessionFuture = + client.async.live.connect( + modelId, LiveConnectConfig.builder().responseModalities(TEXT).build()); + + // Sends and receives messages from the live session. + CompletableFuture responseFuture = + sessionFuture.thenCompose( + session -> { + // A future that completes when the model signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // A variable to concatenate the text response from the model. + StringBuilder modelResponse = new StringBuilder(); + // Starts receiving messages from the live session. + session.receive( + message -> handleLiveServerMessage(message, turnComplete, modelResponse)); + // Sends content to the live session and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose( + unused -> session.close().thenApply(result -> modelResponse.toString())); + }); + + String response = responseFuture.join(); + System.out.println(response); + // Example output: + // > Hello? Gemini, are you there? + // + // Yes, I am here. How can I help you today? + return response; + } + } + + // Sends content to the live session. + private static CompletableFuture sendContent(AsyncSession session) { + String textInput = "Hello? Gemini, are you there?"; + System.out.printf("> %s\n", textInput); + return session.sendClientContent( + LiveSendClientContentParameters.builder() + .turns(Content.builder().role("user").parts(Part.fromText(textInput)).build()) + .turnComplete(true) + .build()); + } + + // Concatenates the output transcription from the model and signals + // `turnComplete` when the model is done generating the response. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete, StringBuilder response) { + message + .serverContent() + .flatMap(LiveServerContent::modelTurn) + .flatMap(Content::parts) + .ifPresent(parts -> parts.forEach(part -> part.text().ifPresent(response::append))); + // Checks if the model's turn is over. + if (message.serverContent().flatMap(LiveServerContent::turnComplete).orElse(false)) { + turnComplete.complete(null); + } + } +} +// [END googlegenaisdk_live_with_txt] diff --git a/genai/snippets/src/test/java/genai/live/LiveIT.java b/genai/snippets/src/test/java/genai/live/LiveIT.java new file mode 100644 index 00000000000..cc8226eb233 --- /dev/null +++ b/genai/snippets/src/test/java/genai/live/LiveIT.java @@ -0,0 +1,89 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package genai.live; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class LiveIT { + + private static final String GEMINI_FLASH_LIVE_PREVIEW = "gemini-2.0-flash-live-preview-04-09"; + private static final String GEMINI_FLASH_LIVE_PREVIEW_NATIVE_AUDIO = + "gemini-live-2.5-flash-preview-native-audio"; + private ByteArrayOutputStream bout; + private PrintStream out; + + // Check if the required environment variables are set. + public static void requireEnvVar(String envVarName) { + assertWithMessage(String.format("Missing environment variable '%s' ", envVarName)) + .that(System.getenv(envVarName)) + .isNotEmpty(); + } + + @BeforeClass + public static void checkRequirements() { + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + } + + @Before + public void setUp() { + bout = new ByteArrayOutputStream(); + out = new PrintStream(bout); + System.setOut(out); + } + + @After + public void tearDown() { + System.setOut(null); + bout.reset(); + } + + @Test + public void testLiveCodeExecWithTxt() { + String response = LiveCodeExecWithTxt.generateContent(GEMINI_FLASH_LIVE_PREVIEW); + assertThat(response).isNotEmpty(); + } + + @Test + public void testLiveGroundGoogSearchWithTxt() { + String response = LiveGroundGoogSearchWithTxt.generateContent(GEMINI_FLASH_LIVE_PREVIEW); + assertThat(response).isNotEmpty(); + } + + @Test + public void testLiveTranscribeWithAudio() { + String response = + LiveTranscribeWithAudio.generateContent(GEMINI_FLASH_LIVE_PREVIEW_NATIVE_AUDIO); + assertThat(response).isNotEmpty(); + } + + @Test + public void testLiveWithTxt() { + String response = LiveWithTxt.generateContent(GEMINI_FLASH_LIVE_PREVIEW); + assertThat(response).isNotEmpty(); + } +}