diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithEnumClassSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithEnumClassSchema.java new file mode 100644 index 00000000000..72dd2abf726 --- /dev/null +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithEnumClassSchema.java @@ -0,0 +1,96 @@ +/* + * 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_enum_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 java.util.List; + +public class ControlledGenerationWithEnumClassSchema { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.5-flash"; + String prompt = "What type of instrument is a guitar?"; + generateContent(modelId, prompt); + } + + // Generates content with an enum 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()) { + + // Build schema using enum values + Schema responseSchema = + Schema.builder() + .type(Type.Known.STRING) + .enum_( + List.of( + InstrumentClass.PERCUSSION.getValue(), + InstrumentClass.STRING.getValue(), + InstrumentClass.WOODWIND.getValue(), + InstrumentClass.BRASS.getValue(), + InstrumentClass.KEYBOARD.getValue())) + .build(); + + GenerateContentConfig config = + GenerateContentConfig.builder() + .responseMimeType("text/x.enum") + .responseSchema(responseSchema) + .build(); + + GenerateContentResponse response = client.models.generateContent(modelId, contents, config); + + System.out.println(response.text()); + // Example response: + // String + return response.text(); + } + } + + // Enum mirroring the Python sample + public enum InstrumentClass { + PERCUSSION("Percussion"), + STRING("String"), + WOODWIND("Woodwind"), + BRASS("Brass"), + KEYBOARD("Keyboard"); + + private final String value; + + InstrumentClass(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} +// [END googlegenaisdk_ctrlgen_with_enum_class_schema] diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNullableSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNullableSchema.java new file mode 100644 index 00000000000..d9fbc5007e0 --- /dev/null +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithNullableSchema.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.controlledgeneration; + +// [START googlegenaisdk_ctrlgen_with_nullable_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; +import java.util.Map; + +public class ControlledGenerationWithNullableSchema { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.5-flash"; + + String prompt = + "The week ahead brings a mix of weather conditions.\n" + + "Sunday is expected to be sunny with a temperature " + + "of 77°F and a humidity level of 50%. " + + "Winds will be light at around 10 km/h.\n" + + "Monday will see partly cloudy skies with " + + "a slightly cooler temperature of 72°F and the winds " + + "will pick up slightly to around 15 km/h.\n" + + "Tuesday brings rain showers, with temperatures dropping " + + "to 64°F and humidity rising to 70%.\n" + + "Wednesday may see thunderstorms, with a temperature of 68°F.\n" + + "Thursday will be cloudy with a temperature of 66°F and moderate humidity at 60%.\n" + + "Friday returns to partly cloudy conditions, with " + + "a temperature of 73°F and the Winds will be " + + "light at 12 km/h.\n" + + "Finally, Saturday rounds off the week with sunny skies, a " + + "temperature of 80°F, and a humidity " + + "level of 40%. Winds will be gentle at 8 km/h.\n"; + + generateContent(modelId, prompt); + } + + // Generates content with a nullable 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()) { + + // Define schema for array items (each weather entry object) + Schema dayForecastSchema = + Schema.builder() + .type(Type.Known.OBJECT) + .properties( + Map.of( + "Day", Schema.builder().type(Type.Known.STRING).nullable(true).build(), + "Forecast", Schema.builder().type(Type.Known.STRING).nullable(true).build(), + "Temperature", + Schema.builder().type(Type.Known.INTEGER).nullable(true).build(), + "Humidity", Schema.builder().type(Type.Known.STRING).nullable(true).build(), + "Wind Speed", + Schema.builder().type(Type.Known.INTEGER).nullable(true).build())) + .required(List.of("Day", "Temperature", "Forecast", "Wind Speed")) + .build(); + + // Full response schema + Schema responseSchema = + Schema.builder() + .type(Type.Known.OBJECT) + .properties( + Map.of( + "forecast", + Schema.builder().type(Type.Known.ARRAY).items(dayForecastSchema).build())) + .build(); + + GenerateContentConfig config = + GenerateContentConfig.builder() + .responseMimeType("application/json") + .responseSchema(responseSchema) + .build(); + + GenerateContentResponse response = client.models.generateContent(modelId, contents, config); + + System.out.println(response.text()); + // Example response: + // {"forecast": [{"Day": "Sunday", "Forecast": "sunny", "Temperature": 77, "Wind Speed": 10, + // "Humidity": "50%"}, + // {"Day": "Monday", "Forecast": "partly cloudy", "Temperature": 72, "Wind Speed": 15}, + // {"Day": "Tuesday", "Forecast": "rain showers", "Temperature": 64, "Wind Speed": null, + // "Humidity": "70%"}, + // {"Day": "Wednesday", "Forecast": "thunderstorms", "Temperature": 68, "Wind Speed": null}, + // {"Day": "Thursday", "Forecast": "cloudy", "Temperature": 66, "Wind Speed": null, + // "Humidity": "60%"}, + // {"Day": "Friday", "Forecast": "partly cloudy", "Temperature": 73, "Wind Speed": 12}, + // {"Day": "Saturday", "Forecast": "sunny", "Temperature": 80, "Wind Speed": 8, "Humidity": + // "40%"}]} + return response.text(); + } + } +} +// [END googlegenaisdk_ctrlgen_with_nullable_schema] diff --git a/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithResponseSchema.java b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithResponseSchema.java new file mode 100644 index 00000000000..97a0b3d2132 --- /dev/null +++ b/genai/snippets/src/main/java/genai/controlledgeneration/ControlledGenerationWithResponseSchema.java @@ -0,0 +1,100 @@ +/* + * 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_resp_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; +import java.util.Map; + +public class ControlledGenerationWithResponseSchema { + + public static void main(String[] args) { + // TODO(developer): Replace these variables before running the sample. + String modelId = "gemini-2.5-flash"; + + String contents = "List a few popular cookie recipes."; + + generateContent(modelId, contents); + } + + // Generates content with a 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 each item in array + 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())) + .required(List.of("recipe_name", "ingredients")) + .build(); + + // Full root schema (array) + Schema responseSchema = Schema.builder().type(Type.Known.ARRAY).items(recipeSchema).build(); + + GenerateContentConfig config = + GenerateContentConfig.builder() + .responseMimeType("application/json") + .responseSchema(responseSchema) + .build(); + + GenerateContentResponse response = client.models.generateContent(modelId, contents, config); + + System.out.println(response.text()); + // Example response: + // [ + // { + // "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": "Chocolate Chip Cookies", + // } + // ] + return response.text(); + } + } +} +// [END googlegenaisdk_ctrlgen_with_resp_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..bdd6fe2c05e 100644 --- a/genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java +++ b/genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java @@ -65,4 +65,50 @@ public void testControlledGenerationWithEnumSchema() { String response = ControlledGenerationWithEnumSchema.generateContent(GEMINI_FLASH, prompt); assertThat(response).isNotEmpty(); } + + @Test + public void testControlledGenerationWithEnumClassSchema() { + String prompt = "What type of instrument is a guitar?"; + String response = ControlledGenerationWithEnumClassSchema.generateContent(GEMINI_FLASH, prompt); + + assertThat(response).isNotEmpty(); + assertThat(response).isEqualTo("String"); + } + + @Test + public void testControlledGenerationWithNullableSchema() { + String prompt = + "The week ahead brings a mix of weather conditions.\n" + + "Sunday is expected to be sunny with a temperature " + + "of 77°F and a humidity level of 50%. " + + "Winds will be light at around 10 km/h.\n" + + "Monday will see partly cloudy skies with " + + "a slightly cooler temperature of 72°F and the winds " + + "will pick up slightly to around 15 km/h.\n" + + "Tuesday brings rain showers, with temperatures dropping " + + "to 64°F and humidity rising to 70%.\n" + + "Wednesday may see thunderstorms, with a temperature of 68°F.\n" + + "Thursday will be cloudy with a temperature of 66°F and moderate humidity at 60%.\n" + + "Friday returns to partly cloudy conditions, with " + + "a temperature of 73°F and the Winds will be " + + "light at 12 km/h.\n" + + "Finally, Saturday rounds off the week with sunny skies, a " + + "temperature of 80°F, and a humidity " + + "level of 40%. Winds will be gentle at 8 km/h.\n"; + + String response = ControlledGenerationWithNullableSchema.generateContent(GEMINI_FLASH, prompt); + + assertThat(response).isNotEmpty(); + assertThat(response).contains("forecast"); + } + + @Test + public void testControlledGenerationWithResponseSchema() { + String prompt = "List a few popular cookie recipes."; + + String response = ControlledGenerationWithResponseSchema.generateContent(GEMINI_FLASH, prompt); + + assertThat(response).isNotEmpty(); + assertThat(response).contains("recipe_name"); + } }