Skip to content

Commit 23c44ea

Browse files
authored
feat(genai): Add samples for controlled generation class and nested schema (#10207)
* genai: added controlled generation class and nested schema samples * genai: PR comments * genai: PR comments
1 parent aecf49e commit 23c44ea

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package genai.controlledgeneration;
18+
19+
// [START googlegenaisdk_ctrlgen_with_class_schema]
20+
21+
import com.google.genai.Client;
22+
import com.google.genai.types.GenerateContentConfig;
23+
import com.google.genai.types.GenerateContentResponse;
24+
import com.google.genai.types.HttpOptions;
25+
import com.google.genai.types.Schema;
26+
import com.google.genai.types.Type;
27+
import com.google.gson.Gson;
28+
import java.util.List;
29+
import java.util.Map;
30+
31+
public class ControlledGenerationWithClassSchema {
32+
33+
private static final Gson gson = new Gson();
34+
35+
public static void main(String[] args) {
36+
// TODO(developer): Replace these variables before running the sample.
37+
String contents = "List a few popular cookie recipes.";
38+
String modelId = "gemini-2.5-flash";
39+
generateContent(modelId, contents);
40+
}
41+
42+
// Generates content with a class response schema
43+
public static String generateContent(String modelId, String contents) {
44+
// Initialize client that will be used to send requests. This client only needs to be created
45+
// once, and can be reused for multiple requests.
46+
try (Client client =
47+
Client.builder()
48+
.location("global")
49+
.vertexAI(true)
50+
.httpOptions(HttpOptions.builder().apiVersion("v1").build())
51+
.build()) {
52+
53+
// Schema for a single Recipe object
54+
Schema recipeSchema =
55+
Schema.builder()
56+
.type(Type.Known.OBJECT)
57+
.properties(
58+
Map.of(
59+
"recipe_name",
60+
Schema.builder().type(Type.Known.STRING).build(),
61+
"ingredients",
62+
Schema.builder()
63+
.type(Type.Known.ARRAY)
64+
.items(Schema.builder().type(Type.Known.STRING).build())
65+
.build()))
66+
.build();
67+
68+
// Schema defining response as an array of Recipe
69+
Schema responseSchema = Schema.builder().type(Type.Known.ARRAY).items(recipeSchema).build();
70+
71+
GenerateContentConfig config =
72+
GenerateContentConfig.builder()
73+
.responseSchema(responseSchema)
74+
.responseMimeType("application/json")
75+
.build();
76+
77+
GenerateContentResponse response = client.models.generateContent(modelId, contents, config);
78+
79+
System.out.println("Raw JSON:\n" + response.text());
80+
81+
// Parse JSON into typed objects
82+
Recipe[] parsed = gson.fromJson(response.text(), Recipe[].class);
83+
84+
System.out.println("\nParsed objects:");
85+
for (Recipe r : parsed) {
86+
System.out.println(r);
87+
}
88+
// Example response:
89+
// [
90+
// {
91+
// "recipe_name": "Classic Chocolate Chip Cookies",
92+
// "ingredients": [
93+
// "2 1/4 cups all-purpose flour",
94+
// "1 teaspoon baking soda",
95+
// "1 teaspoon salt",
96+
// "1 cup (2 sticks) unsalted butter, softened",
97+
// "3/4 cup granulated sugar",
98+
// "3/4 cup packed brown sugar",
99+
// "1 teaspoon vanilla extract",
100+
// "2 large eggs",
101+
// "2 cups chocolate chips"
102+
// ]
103+
// },
104+
// ...
105+
// ]
106+
return response.text();
107+
}
108+
}
109+
110+
public static class Recipe {
111+
@com.google.gson.annotations.SerializedName("recipe_name")
112+
public String recipeName;
113+
114+
public List<String> ingredients;
115+
116+
@Override
117+
public String toString() {
118+
return "Recipe{name='" + recipeName + "', ingredients=" + ingredients + "}";
119+
}
120+
}
121+
}
122+
123+
// [END googlegenaisdk_ctrlgen_with_class_schema]
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package genai.controlledgeneration;
18+
19+
// [START googlegenaisdk_ctrlgen_with_nested_class_schema]
20+
21+
import com.google.genai.Client;
22+
import com.google.genai.types.GenerateContentConfig;
23+
import com.google.genai.types.GenerateContentResponse;
24+
import com.google.genai.types.HttpOptions;
25+
import com.google.genai.types.Schema;
26+
import com.google.genai.types.Type;
27+
import com.google.gson.Gson;
28+
import java.util.List;
29+
import java.util.Map;
30+
31+
public class ControlledGenerationWithNestedSchema {
32+
33+
private static final Gson gson = new Gson();
34+
35+
public static void main(String[] args) {
36+
// TODO(developer): Replace these variables before running the sample.
37+
String contents = "List about 10 home-baked cookies and give them grades based on tastiness.";
38+
String modelId = "gemini-2.5-flash";
39+
generateContent(modelId, contents);
40+
}
41+
42+
// Generates content with a nested response schema
43+
public static String generateContent(String modelId, String contents) {
44+
// Initialize client that will be used to send requests. This client only needs to be created
45+
// once, and can be reused for multiple requests.
46+
try (Client client =
47+
Client.builder()
48+
.location("global")
49+
.vertexAI(true)
50+
.httpOptions(HttpOptions.builder().apiVersion("v1").build())
51+
.build()) {
52+
53+
// Enum schema for Grade
54+
Schema gradeSchema =
55+
Schema.builder()
56+
.type(Type.Known.STRING)
57+
.enum_(List.of("a+", "a", "b", "c", "d", "f"))
58+
.build();
59+
60+
// Schema for Recipe object
61+
Schema recipeSchema =
62+
Schema.builder()
63+
.type(Type.Known.OBJECT)
64+
.properties(
65+
Map.of(
66+
"recipe_name",
67+
Schema.builder().type(Type.Known.STRING).build(),
68+
"rating",
69+
gradeSchema))
70+
.build();
71+
72+
// Response is list of Recipe
73+
Schema responseSchema = Schema.builder().type(Type.Known.ARRAY).items(recipeSchema).build();
74+
75+
GenerateContentConfig config =
76+
GenerateContentConfig.builder()
77+
.responseSchema(responseSchema)
78+
.responseMimeType("application/json")
79+
.build();
80+
81+
GenerateContentResponse response = client.models.generateContent(modelId, contents, config);
82+
83+
System.out.println("Raw JSON:\n" + response.text());
84+
85+
// Deserialize to typed objects
86+
Recipe[] parsed = gson.fromJson(response.text(), Recipe[].class);
87+
88+
System.out.println("\nParsed objects:");
89+
for (Recipe r : parsed) {
90+
System.out.println(r);
91+
}
92+
// Example response:
93+
// [{"rating": "a+", "recipe_name": "Classic Chocolate Chip Cookies"}, ...]
94+
return response.text();
95+
}
96+
}
97+
98+
public enum Grade {
99+
@com.google.gson.annotations.SerializedName("a+")
100+
A_PLUS,
101+
@com.google.gson.annotations.SerializedName("a")
102+
A,
103+
@com.google.gson.annotations.SerializedName("b")
104+
B,
105+
@com.google.gson.annotations.SerializedName("c")
106+
C,
107+
@com.google.gson.annotations.SerializedName("d")
108+
D,
109+
@com.google.gson.annotations.SerializedName("f")
110+
F
111+
}
112+
113+
public static class Recipe {
114+
@com.google.gson.annotations.SerializedName("recipe_name")
115+
public String recipeName;
116+
117+
public Grade rating;
118+
119+
@Override
120+
public String toString() {
121+
return "Recipe{name='" + recipeName + "', rating=" + rating + "}";
122+
}
123+
}
124+
}
125+
126+
// [END googlegenaisdk_ctrlgen_with_nested_class_schema]

genai/snippets/src/test/java/genai/controlledgeneration/ControlledGenerationIT.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,26 @@ public void testControlledGenerationWithResponseSchema() {
111111
assertThat(response).isNotEmpty();
112112
assertThat(response).contains("recipe_name");
113113
}
114+
115+
@Test
116+
public void testControlledGenerationWithClassSchema() {
117+
String prompt = "List a few popular cookie recipes.";
118+
119+
String response = ControlledGenerationWithClassSchema.generateContent(GEMINI_FLASH, prompt);
120+
121+
assertThat(response).isNotEmpty();
122+
assertThat(response).contains("recipe_name");
123+
assertThat(response).contains("ingredients");
124+
}
125+
126+
@Test
127+
public void testControlledGenerationWithNestedClassSchema() {
128+
String prompt = "List about 10 home-baked cookies and give them grades based on tastiness.";
129+
130+
String response = ControlledGenerationWithNestedSchema.generateContent(GEMINI_FLASH, prompt);
131+
132+
assertThat(response).isNotEmpty();
133+
assertThat(response).contains("recipe_name");
134+
assertThat(response).contains("rating");
135+
}
114136
}

0 commit comments

Comments
 (0)