From 66f86270ded9c6a091f9f10eb701396bbed920a6 Mon Sep 17 00:00:00 2001 From: cfloress Date: Fri, 5 Dec 2025 00:11:32 -0300 Subject: [PATCH 1/3] genai: added controlled generation class and nested schema samples --- .../ControlledGenerationWithClassSchema.java | 119 +++++++++++++++++ .../ControlledGenerationWithNestedSchema.java | 126 ++++++++++++++++++ .../ControlledGenerationIT.java | 22 +++ 3 files changed, 267 insertions(+) create mode 100644 genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java create mode 100644 genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java new file mode 100644 index 00000000000..d8e66ca32dd --- /dev/null +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.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.controlledgeneration; + +// [START googlegenaisdk_ctrlgen_with_class_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 com.google.gson.Gson; +import java.util.List; +import java.util.Map; + +public class ControlledGenerationWithClassSchema { + + private static final Gson gson = new Gson(); + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String contents = "List a few popular cookie recipes."; + String modelId = "gemini-2.5-flash"; + generateContent(modelId, contents); + } + + // Generates content with a class response schema + 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() + .location("global") + .vertexAI(true) + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + // Schema for a single Recipe object + Schema recipeSchema = + Schema.builder() + .type(Type.Known.OBJECT) + .properties( + Map.of( + "recipe_name", + Schema.builder().type(Type.Known.STRING).build(), + "ingredients", + Schema.builder() + .type(Type.Known.ARRAY) + .items(Schema.builder().type(Type.Known.STRING).build()) + .build())) + .build(); + + // Schema defining response as an array of Recipe + Schema responseSchema = Schema.builder().type(Type.Known.ARRAY).items(recipeSchema).build(); + + GenerateContentConfig config = + GenerateContentConfig.builder() + .responseSchema(responseSchema) + .responseMimeType("application/json") + .build(); + + GenerateContentResponse response = client.models.generateContent(modelId, contents, config); + + System.out.println("Raw JSON:\n" + response.text()); + + // Parse JSON into typed objects + Recipe[] parsed = gson.fromJson(response.text(), Recipe[].class); + + System.out.println("\nParsed objects:"); + for (Recipe r : parsed) { + System.out.println(r); + } + // Example response: + // [Recipe(recipe_name='Chocolate Chip Cookies', ingredients=['2 1/4 cups all-purpose flour' + // { + // "ingredients": [ + // "2 1/4 cups all-purpose flour", + // "1 teaspoon baking soda", + // "1 teaspoon salt", + // "1 cup (2 sticks) unsalted butter, softened", + // "3/4 cup granulated sugar", + // "3/4 cup packed brown sugar", + // "1 teaspoon vanilla extract", + // "2 large eggs", + // "2 cups chocolate chips" + // ], + // "recipe_name": "Classic Chocolate Chip Cookies" + // }, ... ] + return response.text(); + } + } + + public static class Recipe { + public String recipe_name; + public List ingredients; + + @Override + public String toString() { + return "Recipe{name='" + recipe_name + "', ingredients=" + ingredients + "}"; + } + } +} + +// [END googlegenaisdk_ctrlgen_with_class_schema] diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java new file mode 100644 index 00000000000..874775847e6 --- /dev/null +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.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.controlledgeneration; + +// [START googlegenaisdk_ctrlgen_with_nested_class_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 com.google.gson.Gson; +import java.util.List; +import java.util.Map; + +public class ControlledGenerationWithNestedSchema { + + private static final Gson gson = new Gson(); + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String contents = "List about 10 home-baked cookies and give them grades based on tastiness."; + String modelId = "gemini-2.5-flash"; + generateContent(modelId, contents); + } + + public static String generateContent(String modelId, String contents) { + try (Client client = + Client.builder() + .location("global") + .vertexAI(true) + .httpOptions(HttpOptions.builder().apiVersion("v1").build()) + .build()) { + + // Enum schema for Grade + Schema gradeSchema = + Schema.builder() + .type(Type.Known.STRING) + .enum_(List.of("a+", "a", "b", "c", "d", "f")) + .build(); + + // Schema for Recipe object + Schema recipeSchema = + Schema.builder() + .type(Type.Known.OBJECT) + .properties( + Map.of( + "recipe_name", + Schema.builder().type(Type.Known.STRING).build(), + "rating", + gradeSchema)) + .build(); + + // Response is list of Recipe + Schema responseSchema = Schema.builder().type(Type.Known.ARRAY).items(recipeSchema).build(); + + GenerateContentConfig config = + GenerateContentConfig.builder() + .responseSchema(responseSchema) + .responseMimeType("application/json") + .build(); + + GenerateContentResponse response = client.models.generateContent(modelId, contents, config); + + System.out.println("Raw JSON:\n" + response.text()); + + // Deserialize to typed objects + Recipe[] parsed = gson.fromJson(response.text(), Recipe[].class); + + System.out.println("\nParsed objects:"); + for (Recipe r : parsed) { + System.out.println(r); + } + // Example response: + // [{"rating": "a+", "recipe_name": "Classic Chocolate Chip Cookies"}, ...] + return response.text(); + } + } + + public enum Grade { + A_PLUS("a+"), + A("a"), + B("b"), + C("c"), + D("d"), + F("f"); + + public final String value; + + Grade(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + + public static class Recipe { + public String recipe_name; + public Grade rating; + + @Override + public String toString() { + return "Recipe{name='" + recipe_name + "', rating=" + rating + "}"; + } + } +} + +// [END googlegenaisdk_ctrlgen_with_nested_class_schema] diff --git a/genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java b/genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java index 83980a77446..6b866b7daf2 100644 --- a/genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java +++ b/genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java @@ -65,4 +65,26 @@ public void testControlledGenerationWithEnumSchema() { String response = ControlledGenerationWithEnumSchema.generateContent(GEMINI_FLASH, prompt); assertThat(response).isNotEmpty(); } + + @Test + public void testControlledGenerationWithClassSchema() { + String prompt = "List a few popular cookie recipes."; + + String response = ControlledGenerationWithClassSchema.generateContent(GEMINI_FLASH, prompt); + + assertThat(response).isNotEmpty(); + assertThat(response).contains("recipe_name"); + assertThat(response).contains("ingredients"); + } + + @Test + public void testControlledGenerationWithNestedClassSchema() { + String prompt = "List about 10 home-baked cookies and give them grades based on tastiness."; + + String response = ControlledGenerationWithNestedSchema.generateContent(GEMINI_FLASH, prompt); + + assertThat(response).isNotEmpty(); + assertThat(response).contains("recipe_name"); + assertThat(response).contains("rating"); + } } From 881bbce3e12ecbb0aa3982831d9dfa5e0936c312 Mon Sep 17 00:00:00 2001 From: cfloress Date: Fri, 5 Dec 2025 00:25:38 -0300 Subject: [PATCH 2/3] genai: PR comments --- .../ControlledGenerationWithClassSchema.java | 38 ++++++++++--------- .../ControlledGenerationWithNestedSchema.java | 35 ++++++++--------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java index d8e66ca32dd..0e71455fdfa 100644 --- a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithClassSchema.java @@ -86,32 +86,36 @@ public static String generateContent(String modelId, String contents) { System.out.println(r); } // Example response: - // [Recipe(recipe_name='Chocolate Chip Cookies', ingredients=['2 1/4 cups all-purpose flour' - // { - // "ingredients": [ - // "2 1/4 cups all-purpose flour", - // "1 teaspoon baking soda", - // "1 teaspoon salt", - // "1 cup (2 sticks) unsalted butter, softened", - // "3/4 cup granulated sugar", - // "3/4 cup packed brown sugar", - // "1 teaspoon vanilla extract", - // "2 large eggs", - // "2 cups chocolate chips" - // ], - // "recipe_name": "Classic Chocolate Chip Cookies" - // }, ... ] + // [ + // { + // "recipe_name": "Classic Chocolate Chip Cookies", + // "ingredients": [ + // "2 1/4 cups all-purpose flour", + // "1 teaspoon baking soda", + // "1 teaspoon salt", + // "1 cup (2 sticks) unsalted butter, softened", + // "3/4 cup granulated sugar", + // "3/4 cup packed brown sugar", + // "1 teaspoon vanilla extract", + // "2 large eggs", + // "2 cups chocolate chips" + // ] + // }, + // ... + // ] return response.text(); } } public static class Recipe { - public String recipe_name; + @com.google.gson.annotations.SerializedName("recipe_name") + public String recipeName; + public List ingredients; @Override public String toString() { - return "Recipe{name='" + recipe_name + "', ingredients=" + ingredients + "}"; + return "Recipe{name='" + recipeName + "', ingredients=" + ingredients + "}"; } } } diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java index 874775847e6..95dc78c60e5 100644 --- a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java @@ -93,32 +93,29 @@ public static String generateContent(String modelId, String contents) { } public enum Grade { - A_PLUS("a+"), - A("a"), - B("b"), - C("c"), - D("d"), - F("f"); - - public final String value; - - Grade(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } + @com.google.gson.annotations.SerializedName("a+") + A_PLUS, + @com.google.gson.annotations.SerializedName("a") + A, + @com.google.gson.annotations.SerializedName("b") + B, + @com.google.gson.annotations.SerializedName("c") + C, + @com.google.gson.annotations.SerializedName("d") + D, + @com.google.gson.annotations.SerializedName("f") + F } public static class Recipe { - public String recipe_name; + @com.google.gson.annotations.SerializedName("recipe_name") + public String recipeName; + public Grade rating; @Override public String toString() { - return "Recipe{name='" + recipe_name + "', rating=" + rating + "}"; + return "Recipe{name='" + recipeName + "', rating=" + rating + "}"; } } } From ee1ba9f27653844810a963d393edfedcf1d7a174 Mon Sep 17 00:00:00 2001 From: cfloress Date: Fri, 5 Dec 2025 16:51:44 -0300 Subject: [PATCH 3/3] genai: PR comments --- .../ControlledGenerationWithNestedSchema.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java index 95dc78c60e5..95b46dd9c32 100644 --- a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNestedSchema.java @@ -39,7 +39,10 @@ public static void main(String[] args) { generateContent(modelId, contents); } + // Generates content with a nested response schema 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() .location("global")