From 81a9890bdf2d9265f19281ec9f0502a1417b45ea Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Mon, 19 May 2025 16:00:35 +0200 Subject: [PATCH 1/2] feat(genai): Add new GenAI SDK samples --- .../genai/gemini/CountTokensWithText.java | 46 ++++ .../gemini/CountTokensWithTextAndVideo.java | 47 ++++ .../genai/gemini/GenerateContentStream.java | 43 +++ .../gemini/GenerateContentWithEnumSchema.java | 52 ++++ ...enerateContentWithFunctionDescription.java | 94 +++++++ .../GenerateContentWithSystemInstruction.java | 47 ++++ .../genai/gemini/GenerateContentWithText.java | 42 +++ .../GenerateContentWithTextAndImage.java | 39 +++ .../gemini/GenerateContentWithVideo.java | 51 ++++ .../gemini/TextGenerationWithMultiImage.java | 74 +++++ .../test/java/genai/gemini/SnippetsIT.java | 255 ++++++++++++++++++ 11 files changed, 790 insertions(+) create mode 100644 genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java create mode 100644 genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java create mode 100644 genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java create mode 100644 genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java create mode 100644 genai/snippets/src/test/java/genai/gemini/SnippetsIT.java diff --git a/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java b/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java new file mode 100644 index 00000000000..237bc628cf3 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java @@ -0,0 +1,46 @@ +package snippets; + +// [START googlegenaisdk_counttoken_with_txt] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.CountTokensResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; +import java.util.List; +import java.util.Optional; + +public class CountTokensWithText { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + countTokens(modelId); + } + + public static Optional countTokens(String modelId) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + Content content = Content.builder() + .parts(List.of( + Part.fromText("What's the highest mountain in Africa?"))) + .build(); + + CountTokensResponse response = + client.models.countTokens(modelId, List.of(content), null); + + System.out.print(response); + // Example response: + // CountTokensResponse{totalTokens=Optional[9], cachedContentTokenCount=Optional.empty} + return response.totalTokens(); + } + } +} +// [END googlegenaisdk_counttoken_with_txt] + + + diff --git a/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java b/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java new file mode 100644 index 00000000000..8b59b43b6b2 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java @@ -0,0 +1,47 @@ +package snippets; + +// [START googlegenaisdk_counttoken_with_txt_vid] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.CountTokensResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; +import java.util.List; +import java.util.Optional; + +public class CountTokensWithTextAndVideo { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + countTokens(modelId); + } + + public static Optional countTokens(String modelId) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + Content content = Content.builder() + .parts(List.of( + Part.fromText("Provide a description of this video"), + Part.fromUri("gs://cloud-samples-data/generative-ai/video/pixel8.mp4", "video/mp4"))) + .build(); + + CountTokensResponse response = + client.models.countTokens(modelId, List.of(content), + null); + + System.out.print(response); + // Example response: + // CountTokensResponse{totalTokens=Optional[16251], cachedContentTokenCount=Optional.empty} + return response.totalTokens(); + } + } +} +// [END googlegenaisdk_counttoken_with_txt_vid] + + diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java new file mode 100644 index 00000000000..07f5187a320 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java @@ -0,0 +1,43 @@ +package snippets; + +// [START googlegenaisdk_textgen_with_txt_stream] + +import com.google.genai.Client; +import com.google.genai.ResponseStream; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; + +public class GenerateContentStream { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String contents = "Why is the sky blue?"; + String modelId = "gemini-2.0-flash"; + generateContent(modelId, contents); + } + + public static String generateContent(String modelId, String contents) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + StringBuilder responseTextBuilder = new StringBuilder(); + ResponseStream responseStream = + client.models.generateContentStream(modelId, contents, null); + + for (GenerateContentResponse chunk : responseStream) { + System.out.print(chunk.text()); + responseTextBuilder.append(chunk.text()); + } + // Example response: + // The sky appears blue due to a phenomenon called **Rayleigh scattering**. Here's + // a breakdown of why: + // ... + return responseTextBuilder.toString(); + } + } +} +// [END googlegenaisdk_textgen_with_txt_stream] \ No newline at end of file diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java new file mode 100644 index 00000000000..f67e21ebc55 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java @@ -0,0 +1,52 @@ +package snippets; + +// [START googlegenaisdk_ctrlgen_with_enum_schema] + +import com.google.genai.Client; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Schema; +import com.google.genai.types.Type; +import java.util.List; + +public class GenerateContentWithEnumSchema { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String contents = "What type of instrument is an oboe?"; + String modelId = "gemini-2.0-flash"; + generateContent(modelId, contents); + } + + public static String generateContent(String modelId, String contents) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + // Define the response schema with an enum. + Schema responseSchema = + Schema.builder() + .type(Type.Known.STRING) + .enum_( + List.of("Percussion", "String", "Woodwind", "Brass", "Keyboard")) + .build(); + + GenerateContentConfig config = GenerateContentConfig.builder() + .responseMimeType("text/x.enum") + .responseSchema(responseSchema) + .build(); + + GenerateContentResponse response = + client.models.generateContent(modelId, contents, config); + + System.out.print(response.text()); + // Example response: + // Woodwind + return response.text(); + } + } +} +// [END googlegenaisdk_ctrlgen_with_enum_schema] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java new file mode 100644 index 00000000000..ada692a70fa --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java @@ -0,0 +1,94 @@ +package snippets; + +// [START googlegenaisdk_tools_func_desc_with_txt] + +import com.google.genai.Client; +import com.google.genai.types.FunctionDeclaration; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Schema; +import com.google.genai.types.Tool; +import com.google.genai.types.Type; +import java.util.List; +import java.util.Map; + +public class GenerateContentWithFunctionDescription { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + String contents = + "At Stellar Sounds, a music label, 2024 was a rollercoaster. \"Echoes of the Night,\" a debut synth-pop album, '\n" + + " 'surprisingly sold 350,000 copies, while veteran rock band \"Crimson Tide's\" latest, \"Reckless Hearts,\" '\n" + + " 'lagged at 120,000. Their up-and-coming indie artist, \"Luna Bloom's\" EP, \"Whispers of Dawn,\" '\n" + + " 'secured 75,000 sales. The biggest disappointment was the highly-anticipated rap album \"Street Symphony\" '\n" + + " \"only reaching 100,000 units. Overall, Stellar Sounds moved over 645,000 units this year, revealing unexpected \"\n" + + " \"trends in music consumption."; + + generateContent(modelId, contents); + } + + public static String generateContent(String modelId, String contents) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + FunctionDeclaration getAlbumSales = FunctionDeclaration.builder() + .name("get_album_sales") + .description("Gets the number of albums sold") + // Function parameters are specified in schema format + .parameters(Schema.builder() + .type(Type.Known.OBJECT) + .properties(Map.of( + "albums", Schema.builder() + .type(Type.Known.ARRAY) + .description("List of albums") + .items(Schema.builder() + .description("Album and its sales") + .type(Type.Known.OBJECT) + .properties(Map.of( + "album_name", Schema.builder() + .type(Type.Known.STRING) + .description("Name of the music album") + .build(), + "copies_sold", Schema.builder() + .type(Type.Known.INTEGER) + .description("Number of copies sold") + .build() + )) + .build()) // End items schema for albums + .build() // End "albums" property schema + )) + .build()) // End parameters schema + .build(); // End function declaration + + Tool salesTool = Tool.builder() + .functionDeclarations(List.of(getAlbumSales)) + .build(); + + GenerateContentConfig config = GenerateContentConfig.builder() + .tools(List.of(salesTool)) + .temperature(0.0f) + .build(); + + GenerateContentResponse response = client.models.generateContent( + modelId, + contents, + config); + + // response.functionCalls() returns an Optional>. + // We get the list, then get the first FunctionCall from the list. + System.out.println(response.functionCalls().get(0)); + return response.functionCalls().toString(); + // Example response: + // FunctionCall{id=Optional.empty, args=Optional[{albums=[{copies_sold=350000, album_name=Echoes of the Night}, + // {copies_sold=120000, album_name=Reckless Hearts}, {copies_sold=75000, album_name=Whispers of Dawn}, + // {album_name=Street Symphony, copies_sold=100000}]}], name=Optional[get_album_sales]} + } + } +} +// [END googlegenaisdk_tools_func_desc_with_txt] + diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java new file mode 100644 index 00000000000..7d6838744dc --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java @@ -0,0 +1,47 @@ +package snippets; + +// [START googlegenaisdk_textgen_sys_instr_with_txt] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentConfig; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; + +public class GenerateContentWithSystemInstruction { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + generateContent(modelId); + } + + public static String generateContent(String modelId) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + GenerateContentConfig config = GenerateContentConfig.builder() + .systemInstruction(Content.fromParts( + Part.fromText("You're a language translator."), + Part.fromText("Your mission is to translate text in English to French."))) + .build(); + + GenerateContentResponse response = + client.models.generateContent(modelId, Content.fromParts( + Part.fromText("Why is the sky blue?")), + config); + + System.out.print(response.text()); + // Example response: + // Pourquoi le ciel est-il bleu ? + return response.text(); + } + } +} +// [END googlegenaisdk_textgen_sys_instr_with_txt] + + diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java new file mode 100644 index 00000000000..705bd4359e5 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java @@ -0,0 +1,42 @@ +package snippets; + +// [START googlegenaisdk_textgen_with_txt] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; + +public class GenerateContentWithText { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + generateContent(modelId); + } + + public static String generateContent(String modelId) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + GenerateContentResponse response = + client.models.generateContent(modelId, Content.fromParts( + Part.fromText("How does AI work?")), + null); + + System.out.print(response.text()); + // Example response: + // Okay, let's break down how AI works. It's a broad field, so I'll focus on the ... + // + // Here's a simplified overview: + // ... + return response.text(); + } + } +} +// [END googlegenaisdk_textgen_with_txt] + diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java new file mode 100644 index 00000000000..ad5688fc549 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java @@ -0,0 +1,39 @@ +package snippets; + +// [START googlegenaisdk_textgen_with_txt_img] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; + +public class GenerateContentWithTextAndImage { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + generateContent(modelId); + } + + public static String generateContent(String modelId) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + GenerateContentResponse response = + client.models.generateContent(modelId, Content.fromParts( + Part.fromText("What is shown in this image?"), + Part.fromUri("gs://cloud-samples-data/generative-ai/image/scones.jpg", "image/jpeg")), + null); + + System.out.print(response.text()); + // Example response: + // The image shows a flat lay of blueberry scones arranged on parchment paper. There are ... + return response.text(); + } + } +} +// [END googlegenaisdk_textgen_with_txt_img] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java new file mode 100644 index 00000000000..56a6e6d3029 --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java @@ -0,0 +1,51 @@ +package snippets; + +// [START googlegenaisdk_textgen_with_video] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; + +public class GenerateContentWithVideo { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + String prompt = " Analyze the provided video file, including its audio.\n" + + " Summarize the main points of the video concisely.\n" + + " Create a chapter breakdown with timestamps for key sections or topics discussed."; + generateContent(modelId, prompt); + } + + public static String generateContent(String modelId, String prompt) { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + GenerateContentResponse response = + client.models.generateContent(modelId, Content.fromParts( + Part.fromText(prompt), + Part.fromUri("gs://cloud-samples-data/generative-ai/video/pixel8.mp4", "video/mp4")), + null); + + System.out.print(response.text()); + // Example response: + // Here's a breakdown of the video: + // + // **Summary:** + // + // Saeka Shimada, a photographer in Tokyo, uses the Google Pixel 8 Pro's "Video Boost" feature + // to ... + // + // **Chapter Breakdown with Timestamps:** + // + // * **[00:00-00:12] Introduction & Tokyo at Night:** Saeka Shimada introduces herself ... + return response.text(); + } + } +} +// [END googlegenaisdk_textgen_with_video] diff --git a/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java b/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java new file mode 100644 index 00000000000..56c9f2ed1da --- /dev/null +++ b/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java @@ -0,0 +1,74 @@ +package snippets; + +// [START googlegenaisdk_textgen_with_multi_img] + +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.GenerateContentResponse; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class TextGenerationWithMultiImage { + + public static void main(String[] args) throws IOException { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.0-flash"; + // Content from Google Cloud Storage + String gcsFileImagePath = "gs://cloud-samples-data/generative-ai/image/scones.jpg"; + String localImageFilePath = "test_data/latte.jpg"; + + generateContent(modelId, gcsFileImagePath, localImageFilePath); + } + + public static String generateContent(String modelId, String gcsFileImagePath, + String localImageFilePath) + throws IOException { + // Initialize client that will be used to send requests. This client only needs to be created + // once, and can be reused for multiple requests. + try (Client client = Client.builder() + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + // Read content from a local file. + byte[] localFileImgBytes = Files.readAllBytes(Paths.get(localImageFilePath)); + + GenerateContentResponse response = + client.models.generateContent(modelId, Content.fromParts( + Part.fromText("Generate a list of all the objects contained in both images"), + Part.fromBytes(localFileImgBytes, "image/jpeg"), + Part.fromUri(gcsFileImagePath, "image/jpeg")), + null); + + System.out.print(response.text()); + // Example response: + // Okay, here's the list of objects present in both images: + // + // **Image 1 (Scones):** + // + // * Scones + // * Plate + // * Jam/Preserve + // * Cream/Butter + // * Table/Surface + // * Napkin/Cloth (possibly) + // + // **Image 2 (Latte):** + // + // * Latte/Coffee cup + // * Saucer + // * Spoon + // * Table/Surface + // * Foam/Latte art + // + // **Objects potentially in both (depending on interpretation and specific items):** + // + // * Plate/Saucer (both are serving dishes) + // * Table/Surface + return response.text(); + } + } +} +// [END googlegenaisdk_textgen_with_multi_img] diff --git a/genai/snippets/src/test/java/genai/gemini/SnippetsIT.java b/genai/snippets/src/test/java/genai/gemini/SnippetsIT.java new file mode 100644 index 00000000000..7a17a5c6960 --- /dev/null +++ b/genai/snippets/src/test/java/genai/gemini/SnippetsIT.java @@ -0,0 +1,255 @@ +/* + * Copyright 2023 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. + */ + +// Tests for Gemini code samples. + +package genai.gemini; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.cloud.testing.junit4.MultipleAttemptsRule; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.Base64; +import java.util.stream.Collectors; +import javax.net.ssl.HttpsURLConnection; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class SnippetsIT { + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String LOCATION = "us-central1"; + private static final String GEMINI_FLASH = "gemini-2.0-flash-001"; + private static final String GEMINI_FLASH_1_5 = "gemini-2.0-flash-001"; + private static final String DATASTORE_ID = "grounding-test-datastore_1716831150046"; + private static final int MAX_ATTEMPT_COUNT = 3; + private static final int INITIAL_BACKOFF_MILLIS = 120000; + private static final String TARGET_LANGUAGE_CODE = "fr"; + private static final String TEXT_TO_TRANSLATE = "Hello! How are you doing today?"; + + + // 2 minutes + + @Rule + public final MultipleAttemptsRule multipleAttemptsRule = + new MultipleAttemptsRule(MAX_ATTEMPT_COUNT, INITIAL_BACKOFF_MILLIS); + + private final PrintStream printStream = System.out; + private ByteArrayOutputStream bout; + + // 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 setUp() throws IOException { + try (PrintStream out = System.out) { + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + + requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + stdOut.close(); + System.setOut(out); + } + } + + // Reads the image data from the given URL. + public static byte[] readImageFile(String url) throws IOException { + if (url == null || url.isEmpty()) { + throw new IllegalArgumentException("Invalid URL: " + url); + } + URL urlObj = new URL(url); + HttpsURLConnection connection = null; + InputStream inputStream = null; + ByteArrayOutputStream outputStream = null; + + try { + connection = (HttpsURLConnection) urlObj.openConnection(); + connection.setRequestMethod("GET"); + + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + inputStream = connection.getInputStream(); + outputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + return outputStream.toByteArray(); + } else { + throw new IOException("Error fetching file: " + responseCode); + } + } finally { + if (inputStream != null) { + inputStream.close(); + } + if (outputStream != null) { + outputStream.close(); + } + if (connection != null) { + connection.disconnect(); + } + } + } + + @Before + public void beforeEach() { + bout = new ByteArrayOutputStream(); + System.setOut(new PrintStream(bout)); + } + + @After + public void afterEach() { + System.out.flush(); + System.setOut(printStream); + } + + @Test + public void testSimpleQuestionAnswer() throws Exception { + String output = QuestionAnswer.simpleQuestion(PROJECT_ID, LOCATION, GEMINI_FLASH); + assertThat(output).isNotEmpty(); + } + + @Test + public void testQuickstart() throws IOException { + String output = Quickstart.quickstart(PROJECT_ID, LOCATION, GEMINI_FLASH); + assertThat(output).isNotEmpty(); + } + + @Test + public void testStreamingQuestions() throws Exception { + StreamingQuestionAnswer.streamingQuestion(PROJECT_ID, LOCATION, GEMINI_FLASH); + assertThat(bout.toString()).isNotEmpty(); + } + + @Test + public void testTextInput() throws Exception { + String textPrompt = + "What's a good name for a flower shop that specializes in selling bouquets of" + + " dried flowers?"; + String output = TextInput.textInput(PROJECT_ID, LOCATION, GEMINI_FLASH, textPrompt); + assertThat(output).isNotEmpty(); + } + + @Test + public void testTokenCount() throws Exception { + int tokenCount = GetTokenCount.getTokenCount(PROJECT_ID, LOCATION, GEMINI_FLASH); + assertThat(tokenCount).isEqualTo(6); + } + + @Test + public void testMediaTokenCount() throws Exception { + int tokenCount = GetMediaTokenCount.getMediaTokenCount(PROJECT_ID, LOCATION, GEMINI_FLASH); + assertThat(tokenCount).isEqualTo(16252); + } + + @Test + public void testFunctionCalling() throws Exception { + String textPrompt = "What's the weather in Paris?"; + + String answer = + FunctionCalling.whatsTheWeatherLike(PROJECT_ID, LOCATION, GEMINI_FLASH, textPrompt); + assertThat(answer).ignoringCase().contains("Paris"); + assertThat(answer).ignoringCase().contains("sunny"); + } + + @Test + public void testVideoAudioInput() throws IOException { + String output = VideoInputWithAudio.videoAudioInput(PROJECT_ID, LOCATION, GEMINI_FLASH); + + assertThat(output).ignoringCase().contains("Pixel"); + assertThat(output).ignoringCase().contains("Tokyo"); + } + + // @Test + // public void testGroundingWithPublicData() throws Exception { + // String output = + // GroundingWithPublicData.groundWithPublicData(PROJECT_ID, LOCATION, GEMINI_FLASH_1_5); + + // assertThat(output).ignoringCase().contains("Rayleigh"); + // } + + // @Test + // public void testGroundingWithPrivateData() throws Exception { + // String output = + // GroundingWithPrivateData.groundWithPrivateData( + // PROJECT_ID, + // LOCATION, + // GEMINI_FLASH, + // String.format( + // "projects/%s/locations/global/collections/default_collection/dataStores/%s", + // PROJECT_ID, DATASTORE_ID)); + + // assertThat(output).ignoringCase().contains("DMV"); + // } + + @Test + public void testMultimodalStreaming() throws Exception { + StreamingMultimodal.streamingMultimodal(PROJECT_ID, LOCATION, GEMINI_FLASH); + assertThat(bout.toString()).ignoringCase().contains("no"); + } + + @Test + public void testMultimodalNonStreaming() throws Exception { + String output = Multimodal.nonStreamingMultimodal(PROJECT_ID, LOCATION, GEMINI_FLASH); + + assertThat(output).ignoringCase().contains("no"); + } + + private class Obj { + public String object; + } + + @Test + public void testControlledGenerationWithJsonSchema6() throws Exception { + String output = ControlledGenerationSchema6 + .controlGenerationWithJsonSchema6(PROJECT_ID, LOCATION, GEMINI_FLASH); + + Obj[] objects = new Gson().fromJson(output, Obj[].class); + String recognizedObjects = Arrays.stream(objects) + .map(obj -> obj.object.toLowerCase()) + .collect(Collectors.joining(" ")); + + assertThat(recognizedObjects).isNotEmpty(); + assertThat(recognizedObjects).contains("globe"); + assertThat(recognizedObjects).contains("keyboard"); + assertThat(recognizedObjects).contains("passport"); + assertThat(recognizedObjects).contains("pot"); + } + +} From 65a66392df34900f2172af4885e79f32a08a5b8a Mon Sep 17 00:00:00 2001 From: Sampath Kumar Date: Mon, 19 May 2025 16:06:02 +0200 Subject: [PATCH 2/2] docs(genai): Add Licence header --- .../java/genai/gemini/CountTokensWithText.java | 16 ++++++++++++++++ .../gemini/CountTokensWithTextAndVideo.java | 16 ++++++++++++++++ .../java/genai/gemini/GenerateContentStream.java | 16 ++++++++++++++++ .../gemini/GenerateContentWithEnumSchema.java | 16 ++++++++++++++++ .../GenerateContentWithFunctionDescription.java | 16 ++++++++++++++++ .../GenerateContentWithSystemInstruction.java | 16 ++++++++++++++++ .../genai/gemini/GenerateContentWithText.java | 16 ++++++++++++++++ .../gemini/GenerateContentWithTextAndImage.java | 16 ++++++++++++++++ .../genai/gemini/GenerateContentWithVideo.java | 16 ++++++++++++++++ .../gemini/TextGenerationWithMultiImage.java | 16 ++++++++++++++++ 10 files changed, 160 insertions(+) diff --git a/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java b/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java index 237bc628cf3..e7133dde092 100644 --- a/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java +++ b/genai/snippets/src/main/java/genai/gemini/CountTokensWithText.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_counttoken_with_txt] diff --git a/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java b/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java index 8b59b43b6b2..03331247098 100644 --- a/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java +++ b/genai/snippets/src/main/java/genai/gemini/CountTokensWithTextAndVideo.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_counttoken_with_txt_vid] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java index 07f5187a320..7def06a7f51 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentStream.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_textgen_with_txt_stream] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java index f67e21ebc55..73ab4ff6fc6 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithEnumSchema.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_ctrlgen_with_enum_schema] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java index ada692a70fa..6f6b5f02074 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithFunctionDescription.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_tools_func_desc_with_txt] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java index 7d6838744dc..5f02716b55a 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithSystemInstruction.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_textgen_sys_instr_with_txt] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java index 705bd4359e5..d36abb54bbb 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithText.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_textgen_with_txt] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java index ad5688fc549..6720d5c38f6 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithTextAndImage.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_textgen_with_txt_img] diff --git a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java index 56a6e6d3029..428cea3deff 100644 --- a/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java +++ b/genai/snippets/src/main/java/genai/gemini/GenerateContentWithVideo.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_textgen_with_video] diff --git a/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java b/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java index 56c9f2ed1da..5df2114b9cd 100644 --- a/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java +++ b/genai/snippets/src/main/java/genai/gemini/TextGenerationWithMultiImage.java @@ -1,3 +1,19 @@ +/* + * 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 snippets; // [START googlegenaisdk_textgen_with_multi_img]