Skip to content

Commit 038aa5c

Browse files
feat(genai sdk): controlled text generation
1 parent 8f59180 commit 038aa5c

File tree

7 files changed

+529
-1
lines changed

7 files changed

+529
-1
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package controlled_generation
16+
17+
import (
18+
"bytes"
19+
"testing"
20+
21+
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
22+
)
23+
24+
func TestTextGeneration(t *testing.T) {
25+
tc := testutil.SystemTest(t)
26+
27+
t.Setenv("GOOGLE_GENAI_USE_VERTEXAI", "1")
28+
t.Setenv("GOOGLE_CLOUD_LOCATION", "us-central1")
29+
t.Setenv("GOOGLE_CLOUD_PROJECT", tc.ProjectID)
30+
31+
buf := new(bytes.Buffer)
32+
33+
t.Run("generate with enum schema", func(t *testing.T) {
34+
buf.Reset()
35+
err := generateWithEnumSchema(buf)
36+
if err != nil {
37+
t.Fatalf("generateWithEnumSchema failed: %v", err)
38+
}
39+
40+
output := buf.String()
41+
if output == "" {
42+
t.Error("expected non-empty output, got empty")
43+
}
44+
})
45+
46+
t.Run("generate with response schema", func(t *testing.T) {
47+
buf.Reset()
48+
err := generateWithRespSchema(buf)
49+
if err != nil {
50+
t.Fatalf("generateWithRespSchema failed: %v", err)
51+
}
52+
53+
output := buf.String()
54+
if output == "" {
55+
t.Error("expected non-empty output, got empty")
56+
}
57+
})
58+
59+
t.Run("generate with response schema with nullable values", func(t *testing.T) {
60+
buf.Reset()
61+
err := generateWithNullables(buf)
62+
if err != nil {
63+
t.Fatalf("generateWithNullables failed: %v", err)
64+
}
65+
66+
output := buf.String()
67+
if output == "" {
68+
t.Error("expected non-empty output, got empty")
69+
}
70+
})
71+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package controlled_generation shows how to use the GenAI SDK to generate text that adheres to a specific schema.
16+
package controlled_generation
17+
18+
// [START googlegenaisdk_ctrlgen_with_enum_schema]
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
24+
genai "google.golang.org/genai"
25+
)
26+
27+
// generateWithEnumSchema shows how to use enum schema to generate output.
28+
func generateWithEnumSchema(w io.Writer) error {
29+
ctx := context.Background()
30+
31+
client, err := genai.NewClient(ctx, &genai.ClientConfig{
32+
HTTPOptions: genai.HTTPOptions{APIVersion: "v1"},
33+
})
34+
if err != nil {
35+
return fmt.Errorf("failed to create genai client: %w", err)
36+
}
37+
38+
modelName := "gemini-2.0-flash-001"
39+
contents := []*genai.Content{
40+
{Parts: []*genai.Part{
41+
{Text: "What type of instrument is an oboe?"},
42+
}},
43+
}
44+
config := &genai.GenerateContentConfig{
45+
ResponseMIMEType: "text/x.enum",
46+
ResponseSchema: &genai.Schema{
47+
Type: "STRING",
48+
Enum: []string{"Percussion", "String", "Woodwind", "Brass", "Keyboard"},
49+
},
50+
}
51+
52+
resp, err := client.Models.GenerateContent(ctx, modelName, contents, config)
53+
if err != nil {
54+
return fmt.Errorf("failed to generate content: %w", err)
55+
}
56+
57+
respText, err := resp.Text()
58+
if err != nil {
59+
return fmt.Errorf("failed to convert model response to text: %w", err)
60+
}
61+
fmt.Fprintln(w, respText)
62+
63+
// Example response:
64+
// Woodwind
65+
66+
return nil
67+
}
68+
69+
// [END googlegenaisdk_ctrlgen_with_enum_schema]
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package controlled_generation shows how to use the GenAI SDK to generate text that adheres to a specific schema.
16+
package controlled_generation
17+
18+
// [START googlegenaisdk_ctrlgen_with_nullable_schema]
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
24+
genai "google.golang.org/genai"
25+
)
26+
27+
// generateWithNullables shows how to use the response schema with nullable values.
28+
func generateWithNullables(w io.Writer) error {
29+
ctx := context.Background()
30+
31+
client, err := genai.NewClient(ctx, &genai.ClientConfig{
32+
HTTPOptions: genai.HTTPOptions{APIVersion: "v1"},
33+
})
34+
if err != nil {
35+
return fmt.Errorf("failed to create genai client: %w", err)
36+
}
37+
38+
modelName := "gemini-2.0-flash-001"
39+
prompt := `
40+
The week ahead brings a mix of weather conditions.
41+
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.
42+
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.
43+
Tuesday brings rain showers, with temperatures dropping to 64°F and humidity rising to 70%.
44+
Wednesday may see thunderstorms, with a temperature of 68°F.
45+
Thursday will be cloudy with a temperature of 66°F and moderate humidity at 60%.
46+
Friday returns to partly cloudy conditions, with a temperature of 73°F and the Winds will be light at 12 km/h.
47+
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.
48+
`
49+
contents := []*genai.Content{
50+
{Parts: []*genai.Part{
51+
{Text: prompt},
52+
}},
53+
}
54+
config := &genai.GenerateContentConfig{
55+
ResponseMIMEType: "application/json",
56+
// See the OpenAPI specification for more details and examples:
57+
// https://spec.openapis.org/oas/v3.0.3.html#schema-object
58+
ResponseSchema: &genai.Schema{
59+
Type: "object",
60+
Properties: map[string]*genai.Schema{
61+
"forecast": {
62+
Type: "array",
63+
Items: &genai.Schema{
64+
Type: "object",
65+
Properties: map[string]*genai.Schema{
66+
"Day": {Type: "string", Nullable: true},
67+
"Forecast": {Type: "string", Nullable: true},
68+
"Temperature": {Type: "integer", Nullable: true},
69+
"Humidity": {Type: "string", Nullable: true},
70+
"Wind Speed": {Type: "integer", Nullable: true},
71+
},
72+
Required: []string{"Day", "Temperature", "Forecast", "Wind Speed"},
73+
},
74+
},
75+
},
76+
},
77+
}
78+
79+
resp, err := client.Models.GenerateContent(ctx, modelName, contents, config)
80+
if err != nil {
81+
return fmt.Errorf("failed to generate content: %w", err)
82+
}
83+
84+
respText, err := resp.Text()
85+
if err != nil {
86+
return fmt.Errorf("failed to convert model response to text: %w", err)
87+
}
88+
fmt.Fprintln(w, respText)
89+
90+
// Example response:
91+
// {
92+
// "forecast": [
93+
// {"Day": "Sunday", "Forecast": "Sunny", "Temperature": 77, "Wind Speed": 10, "Humidity": "50%"},
94+
// {"Day": "Monday", "Forecast": "Partly Cloudy", "Temperature": 72, "Wind Speed": 15},
95+
// {"Day": "Tuesday", "Forecast": "Rain Showers", "Temperature": 64, "Wind Speed": null, "Humidity": "70%"},
96+
// {"Day": "Wednesday", "Forecast": "Thunderstorms", "Temperature": 68, "Wind Speed": null},
97+
// {"Day": "Thursday", "Forecast": "Cloudy", "Temperature": 66, "Wind Speed": null, "Humidity": "60%"},
98+
// {"Day": "Friday", "Forecast": "Partly Cloudy", "Temperature": 73, "Wind Speed": 12},
99+
// {"Day": "Saturday", "Forecast": "Sunny", "Temperature": 80, "Wind Speed": 8, "Humidity": "40%"}
100+
// ]
101+
// }
102+
103+
return nil
104+
}
105+
106+
// [END googlegenaisdk_ctrlgen_with_nullable_schema]
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package controlled_generation shows how to use the GenAI SDK to generate text that adheres to a specific schema.
16+
package controlled_generation
17+
18+
// [START googlegenaisdk_ctrlgen_with_resp_schema]
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
24+
genai "google.golang.org/genai"
25+
)
26+
27+
// generateWithRespSchema shows how to use a response schema to generate output in a specific format.
28+
func generateWithRespSchema(w io.Writer) error {
29+
ctx := context.Background()
30+
31+
client, err := genai.NewClient(ctx, &genai.ClientConfig{
32+
HTTPOptions: genai.HTTPOptions{APIVersion: "v1"},
33+
})
34+
if err != nil {
35+
return fmt.Errorf("failed to create genai client: %w", err)
36+
}
37+
38+
config := &genai.GenerateContentConfig{
39+
ResponseMIMEType: "application/json",
40+
// See the OpenAPI specification for more details and examples:
41+
// https://spec.openapis.org/oas/v3.0.3.html#schema-object
42+
ResponseSchema: &genai.Schema{
43+
Type: "array",
44+
Items: &genai.Schema{
45+
Type: "object",
46+
Properties: map[string]*genai.Schema{
47+
"recipe_name": {Type: "string"},
48+
"ingredients": {
49+
Type: "array",
50+
Items: &genai.Schema{Type: "string"},
51+
},
52+
},
53+
Required: []string{"recipe_name", "ingredients"},
54+
},
55+
},
56+
}
57+
contents := []*genai.Content{
58+
{Parts: []*genai.Part{
59+
{Text: "List a few popular cookie recipes."},
60+
}},
61+
}
62+
modelName := "gemini-2.0-flash-001"
63+
64+
resp, err := client.Models.GenerateContent(ctx, modelName, contents, config)
65+
if err != nil {
66+
return fmt.Errorf("failed to generate content: %w", err)
67+
}
68+
69+
respText, err := resp.Text()
70+
if err != nil {
71+
return fmt.Errorf("failed to convert model response to text: %w", err)
72+
}
73+
fmt.Fprintln(w, respText)
74+
75+
// Example response:
76+
// [
77+
// {
78+
// "ingredients": [
79+
// "2 1/4 cups all-purpose flour",
80+
// "1 teaspoon baking soda",
81+
// ...
82+
// ],
83+
// "recipe_name": "Chocolate Chip Cookies"
84+
// },
85+
// {
86+
// ...
87+
// },
88+
// ...
89+
// ]
90+
91+
return nil
92+
}
93+
94+
// [END googlegenaisdk_ctrlgen_with_resp_schema]

0 commit comments

Comments
 (0)