From d2cc19219df1be0ce579f627b0789b12d1b0b6e3 Mon Sep 17 00:00:00 2001 From: Juan Dominguez Date: Thu, 30 Oct 2025 16:15:45 -0300 Subject: [PATCH 1/2] feat(genai): add live samples(1) --- genai/snippets/pom.xml | 6 +- .../java/genai/live/LiveCodeExecWithTxt.java | 117 +++++++++++++++++ .../live/LiveGroundGoogSearchWithTxt.java | 110 ++++++++++++++++ .../genai/live/LiveTranscribeWithAudio.java | 119 ++++++++++++++++++ .../src/main/java/genai/live/LiveWithTxt.java | 108 ++++++++++++++++ .../src/test/java/genai/live/LiveIT.java | 105 ++++++++++++++++ 6 files changed, 560 insertions(+), 5 deletions(-) create mode 100644 genai/snippets/src/main/java/genai/live/LiveCodeExecWithTxt.java create mode 100644 genai/snippets/src/main/java/genai/live/LiveGroundGoogSearchWithTxt.java create mode 100644 genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java create mode 100644 genai/snippets/src/main/java/genai/live/LiveWithTxt.java create mode 100644 genai/snippets/src/test/java/genai/live/LiveIT.java diff --git a/genai/snippets/pom.xml b/genai/snippets/pom.xml index 3c59e1fde38..2adf3e16ea9 100644 --- a/genai/snippets/pom.xml +++ b/genai/snippets/pom.xml @@ -51,17 +51,13 @@ com.google.genai google-genai - 1.23.0 + 1.25.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..cec3a19cc1f --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveCodeExecWithTxt.java @@ -0,0 +1,117 @@ +/* + * 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 void 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 server. + sessionFuture + .thenCompose( + session -> { + // A future that completes when the server signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // Starts receiving messages from the live server. + session.receive(message -> handleLiveServerMessage(message, turnComplete)); + // Sends content to the server and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose(unused -> session.close()); + }) + .join(); + + // Example output: + // > Compute the largest prime palindrome under 100000 + // text: Okay, I need + // text: to find the largest prime palindrome less than 100000... + // code: ExecutableCode{code=Optional[def is_prime(n):... + // result: CodeExecutionResult{outcome=Optional[OUTCOME_OK], + // output=Optional[largest_prime_palindrome=98689... + // The model is done generating. + } + } + + // Sends content to the server. + 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()); + } + + // Prints response messages from the server and signals `turnComplete` when the server is done. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete) { + message + .serverContent() + .flatMap(LiveServerContent::modelTurn) + .flatMap(Content::parts) + .ifPresent( + parts -> + parts.forEach( + part -> { + part.text().ifPresent(text -> System.out.println("text: " + text)); + part.executableCode().ifPresent(code -> System.out.println("code: " + code)); + part.codeExecutionResult() + .ifPresent(result -> System.out.println("result: " + result)); + })); + + if (message.serverContent().flatMap(LiveServerContent::turnComplete).orElse(false)) { + System.out.println("The model is done generating."); + 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..3795a8bd910 --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveGroundGoogSearchWithTxt.java @@ -0,0 +1,110 @@ +/* + * 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 void 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 server. + sessionFuture + .thenCompose( + session -> { + // A future that completes when the server signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // Starts receiving messages from the live server. + session.receive(message -> handleLiveServerMessage(message, turnComplete)); + // Sends content to the server and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose(unused -> session.close()); + }) + .join(); + // Example response: + // > When did the last Brazil vs. Argentina soccer match happen? + // Output: The last Brazil vs + // Output: . Argentina soccer match was on March 25, 2025 + // Output: , a World Cup Qualifying match that Argentina won 4-1. Quite a game. + // The model is done generating. + } + } + + // Sends content to the live server. + 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()); + } + + // Prints response messages from the server and signals `turnComplete` when the server is done. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete) { + message + .serverContent() + .flatMap(LiveServerContent::modelTurn) + .flatMap(Content::parts) + .ifPresent( + parts -> + parts.forEach( + part -> + part.text().ifPresent(text -> System.out.println("Output: " + text)))); + + if (message.serverContent().flatMap(LiveServerContent::turnComplete).orElse(false)) { + System.out.println("The model is done generating."); + 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..c665c8db6c2 --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java @@ -0,0 +1,119 @@ +/* + * 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 void 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 server. + sessionFuture + .thenCompose( + session -> { + // A future that completes when the server signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // Starts receiving messages from the live server. + session.receive(message -> handleLiveServerMessage(message, turnComplete)); + // Sends content to the server and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose(unused -> session.close()); + }) + .join(); + // Example output: + // > Hello? Gemini, are you there? + // Output transcript: Yes, I'm here + // Output transcript: How can I help you today? + // ... + // The model is done generating. + } + } + + // Sends content to the live server. + 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()); + } + + // Prints response messages from the server and signals `turnComplete` when the server is done. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete) { + + if (message.serverContent().isPresent()) { + LiveServerContent serverContent = message.serverContent().get(); + 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(text -> System.out.println("Output transcript: " + text)); + + if (serverContent.turnComplete().orElse(false)) { + System.out.println("The model is done generating."); + 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..4b5c3090a45 --- /dev/null +++ b/genai/snippets/src/main/java/genai/live/LiveWithTxt.java @@ -0,0 +1,108 @@ +/* + * 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 server. + public static void 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 server. + sessionFuture + .thenCompose( + session -> { + // A future that completes when the server signals the end of its turn. + CompletableFuture turnComplete = new CompletableFuture<>(); + // Starts receiving messages from the live server. + session.receive(message -> handleLiveServerMessage(message, turnComplete)); + // Sends content to the server and waits for the turn to complete. + return sendContent(session) + .thenCompose(unused -> turnComplete) + .thenCompose(unused -> session.close()); + }) + .join(); + // Example output: + // > Hello? Gemini, are you there? + // Output: Yes + // Output: , I'm here. How can I help you today? + // The model is done generating. + } + } + + // Sends content to the server. + 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()); + } + + // Prints response messages from the server and signals `turnComplete` when the server is done. + private static void handleLiveServerMessage( + LiveServerMessage message, CompletableFuture turnComplete) { + message + .serverContent() + .flatMap(LiveServerContent::modelTurn) + .flatMap(Content::parts) + .ifPresent( + parts -> + parts.forEach( + part -> part.text().ifPresent(text -> System.out.println("Output: " + text)))); + + if (message.serverContent().flatMap(LiveServerContent::turnComplete).orElse(false)) { + System.out.println("The model is done generating."); + 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..315edf9b8d3 --- /dev/null +++ b/genai/snippets/src/test/java/genai/live/LiveIT.java @@ -0,0 +1,105 @@ +/* + * 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.IOException; +import java.io.PrintStream; +import java.util.List; +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() { + LiveCodeExecWithTxt.generateContent(GEMINI_FLASH_LIVE_PREVIEW); + String output = bout.toString(); + assertThat(output).contains("> Compute the largest prime palindrome under 100000"); + assertThat(output).contains("text:"); + assertThat(output).contains("code: ExecutableCode{code="); + assertThat(output).contains("result: CodeExecutionResult{outcome"); + assertThat(output).contains("The model is done generating."); + } + + @Test + public void testLiveGroundGoogSearchWithTxt() { + LiveGroundGoogSearchWithTxt.generateContent(GEMINI_FLASH_LIVE_PREVIEW); + String output = bout.toString(); + assertThat(output).contains("> When did the last Brazil vs. Argentina soccer match happen?"); + assertThat(output).contains("Output:"); + assertThat(output).contains("The model is done generating."); + } + + @Test + public void testLiveTranscribeWithAudio() { + LiveTranscribeWithAudio.generateContent(GEMINI_FLASH_LIVE_PREVIEW_NATIVE_AUDIO); + String output = bout.toString(); + assertThat(output).contains("> Hello? Gemini, are you there?"); + assertThat(output).contains("Model turn:"); + assertThat(output).contains("Output transcript:"); + assertThat(output).contains("The model is done generating."); + } + + @Test + public void testLiveWithTxt() { + LiveWithTxt.generateContent(GEMINI_FLASH_LIVE_PREVIEW); + String output = bout.toString(); + assertThat(output).contains("> Hello? Gemini, are you there?"); + assertThat(output).contains("Output:"); + assertThat(output).contains("The model is done generating."); + } +} From 327fca426e651105a4e2de10ec189bcb4386eb88 Mon Sep 17 00:00:00 2001 From: Juan Dominguez Date: Thu, 30 Oct 2025 17:30:19 -0300 Subject: [PATCH 2/2] refactor(genai): change way of handling live server messages --- .../genai/live/LiveTranscribeWithAudio.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java b/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java index c665c8db6c2..3d477372a94 100644 --- a/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java +++ b/genai/snippets/src/main/java/genai/live/LiveTranscribeWithAudio.java @@ -93,27 +93,29 @@ private static CompletableFuture sendContent(AsyncSession session) { private static void handleLiveServerMessage( LiveServerMessage message, CompletableFuture turnComplete) { - if (message.serverContent().isPresent()) { - LiveServerContent serverContent = message.serverContent().get(); - serverContent - .modelTurn() - .ifPresent(modelTurn -> System.out.println("Model turn: " + modelTurn.parts())); + 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 + .inputTranscription() + .flatMap(Transcription::text) + .ifPresent(text -> System.out.println("Input transcript: " + text)); - serverContent - .outputTranscription() - .flatMap(Transcription::text) - .ifPresent(text -> System.out.println("Output transcript: " + text)); + serverContent + .outputTranscription() + .flatMap(Transcription::text) + .ifPresent(text -> System.out.println("Output transcript: " + text)); - if (serverContent.turnComplete().orElse(false)) { - System.out.println("The model is done generating."); - turnComplete.complete(null); - } - } + if (serverContent.turnComplete().orElse(false)) { + System.out.println("The model is done generating."); + turnComplete.complete(null); + } + }); } } // [END googlegenaisdk_live_transcribe_with_audio]