Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,17 @@ func TestTextGeneration(t *testing.T) {
t.Error("expected non-empty output, got empty")
}
})

t.Run("generate with nested class response schema", func(t *testing.T) {
buf.Reset()
err := generateWithNestedClassSchema(buf)
if err != nil {
t.Fatalf("generateWithNestedClassSchema failed: %v", err)
}

output := buf.String()
if output == "" {
t.Error("expected non-empty output, got empty")
}
})
}
4 changes: 2 additions & 2 deletions genai/controlled_generation/ctrlgen_with_class_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
)

// Recipe represents the schema for a recipe response.
type Recipe struct {
type RecipeClass struct {
RecipeName string `json:"recipe_name"`
Ingredients []string `json:"ingredients"`
}
Expand Down Expand Up @@ -81,7 +81,7 @@ func generateWithClassSchema(w io.Writer) error {
fmt.Fprintln(w, resp.Text())

// Parse JSON into Go structs
var recipes []Recipe
var recipes []RecipeClass
if err := json.Unmarshal([]byte(resp.Text()), &recipes); err != nil {
return fmt.Errorf("failed to parse response JSON: %w", err)
}
Expand Down
98 changes: 98 additions & 0 deletions genai/controlled_generation/ctrlgen_with_nested_class_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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
//
// https://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 controlled_generation shows how to use the GenAI SDK to generate text that adheres to a specific schema.
package controlled_generation

// [START googlegenaisdk_ctrlgen_with_nested_class_schema]
import (
"context"
"fmt"
"io"

"google.golang.org/genai"
)

type Grade string

const (
APlus Grade = "a+"
A Grade = "a"
B Grade = "b"
C Grade = "c"
D Grade = "d"
F Grade = "f"
)

type Recipe struct {
RecipeName string `json:"recipe_name"`
Rating Grade `json:"rating"`
}

// generateWithNestedClassSchema shows how to use nested class schema to generate output.
func generateWithNestedClassSchema(w io.Writer) error {
ctx := context.Background()

client, err := genai.NewClient(ctx, &genai.ClientConfig{
HTTPOptions: genai.HTTPOptions{APIVersion: "v1"},
})
if err != nil {
return fmt.Errorf("failed to create genai client: %w", err)
}

modelName := "gemini-2.5-flash"
contents := []*genai.Content{
{Parts: []*genai.Part{
{Text: "List about 10 home-baked cookies and give them grades based on tastiness."},
}, Role: "user"},
}

config := &genai.GenerateContentConfig{
ResponseMIMEType: "application/json",
ResponseSchema: &genai.Schema{
Type: genai.TypeArray,
Items: &genai.Schema{
Type: genai.TypeObject,
Properties: map[string]*genai.Schema{
"recipe_name": {Type: genai.TypeString},
"rating": {
Type: genai.TypeString,
Enum: []string{string(APlus), string(A), string(B), string(C), string(D), string(F)},
},
},
Required: []string{"recipe_name", "rating"},
},
},
}

resp, err := client.Models.GenerateContent(ctx, modelName, contents, config)
if err != nil {
return fmt.Errorf("failed to generate content: %w", err)
}

fmt.Fprintln(w, resp.Text())

// Example response:
// [
// {"rating":"a+","recipe_name":"Chocolate Chip Cookies"},
// {"rating":"a","recipe_name":"Oatmeal Raisin Cookies"},
// {"rating":"a+","recipe_name":"Peanut Butter Cookies"},
// {"rating":"b","recipe_name":"Snickerdoodle Cookies"},
// ...
// ]

return nil
}

// [END googlegenaisdk_ctrlgen_with_nested_class_schema]
116 changes: 116 additions & 0 deletions genai/safety/safety_with_txt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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
//
// https://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 safety shows how to use the GenAI SDK to generate safety content.
package safety

// [START googlegenaisdk_safety_with_txt]
import (
"context"
"fmt"
"io"

"google.golang.org/genai"
)

// generateTextWithSafety shows how to apply safety settings to a text generation request.
func generateTextWithSafety(w io.Writer) error {
ctx := context.Background()

client, err := genai.NewClient(ctx, &genai.ClientConfig{
HTTPOptions: genai.HTTPOptions{APIVersion: "v1"},
})
if err != nil {
return fmt.Errorf("failed to create genai client: %w", err)
}

systemInstruction := &genai.Content{
Parts: []*genai.Part{
{Text: "Be as mean as possible."},
},
Role: "user",
}

prompt := "Write a list of 5 disrespectful things that I might say to the universe after stubbing my toe in the dark."

safetySettings := []*genai.SafetySetting{
{Category: genai.HarmCategoryDangerousContent, Threshold: genai.HarmBlockThresholdBlockLowAndAbove},
{Category: genai.HarmCategoryHarassment, Threshold: genai.HarmBlockThresholdBlockLowAndAbove},
{Category: genai.HarmCategoryHateSpeech, Threshold: genai.HarmBlockThresholdBlockLowAndAbove},
{Category: genai.HarmCategorySexuallyExplicit, Threshold: genai.HarmBlockThresholdBlockLowAndAbove},
}

config := &genai.GenerateContentConfig{
SystemInstruction: systemInstruction,
SafetySettings: safetySettings,
}
modelName := "gemini-2.5-flash"
resp, err := client.Models.GenerateContent(ctx, modelName,
[]*genai.Content{{Parts: []*genai.Part{{Text: prompt}}, Role: "user"}},
config,
)
if err != nil {
return fmt.Errorf("failed to generate content: %w", err)
}

fmt.Fprintln(w, resp.Text())

if len(resp.Candidates) > 0 {
fmt.Fprintln(w, "Finish Reason:", resp.Candidates[0].FinishReason)

for _, rating := range resp.Candidates[0].SafetyRatings {
fmt.Fprintf(w, "\nCategory: %v\nIs Blocked: %v\nProbability: %v\nProbability Score: %v\nSeverity: %v\nSeverity Score: %v\n",
rating.Category,
rating.Blocked,
rating.Probability,
rating.ProbabilityScore,
rating.Severity,
rating.SeverityScore,
)
}
}

// Example response:
// Category: HARM_CATEGORY_HATE_SPEECH
// Is Blocked: false
// Probability: NEGLIGIBLE
// Probability Score: 8.996795e-06
// Severity: HARM_SEVERITY_NEGLIGIBLE
// Severity Score: 0.04771039
//
// Category: HARM_CATEGORY_DANGEROUS_CONTENT
// Is Blocked: false
// Probability: NEGLIGIBLE
// Probability Score: 2.2431707e-06
// Severity: HARM_SEVERITY_NEGLIGIBLE
// Severity Score: 0
//
// Category: HARM_CATEGORY_HARASSMENT
// Is Blocked: false
// Probability: NEGLIGIBLE
// Probability Score: 0.00026123362
// Severity: HARM_SEVERITY_NEGLIGIBLE
// Severity Score: 0.022358216
//
// Category: HARM_CATEGORY_SEXUALLY_EXPLICIT
// Is Blocked: false
// Probability: NEGLIGIBLE
// Probability Score: 6.1352006e-07
// Severity: HARM_SEVERITY_NEGLIGIBLE
// Severity Score: 0.020111412

return nil
}

// [END googlegenaisdk_safety_with_txt]
46 changes: 46 additions & 0 deletions genai/safety/safety_with_txt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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 safety

import (
"bytes"
"testing"

"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
)

func TestSafetyGeneration(t *testing.T) {
tc := testutil.SystemTest(t)

t.Setenv("GOOGLE_GENAI_USE_VERTEXAI", "1")
t.Setenv("GOOGLE_CLOUD_LOCATION", "us-central1")
t.Setenv("GOOGLE_CLOUD_PROJECT", tc.ProjectID)

buf := new(bytes.Buffer)

t.Run("generate Text content with safety", func(t *testing.T) {
buf.Reset()
err := generateTextWithSafety(buf)
if err != nil {
t.Fatalf("generateTextWithSafety failed: %v", err)
}

output := buf.String()
if output == "" {
t.Error("expected non-empty output, got empty")
}
})

}