Skip to content

Commit 2c9f7d4

Browse files
authored
feat(genai): Add samples for live ground, func call and structured generation (#5396)
* genai: added live ground,func call and structured generation samples * genai: PR comments * genai: fix go.mod * genai: fix unit test mock
1 parent e399d43 commit 2c9f7d4

File tree

5 files changed

+462
-1
lines changed

5 files changed

+462
-1
lines changed

genai/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.24.0
44

55
require (
66
github.com/GoogleCloudPlatform/golang-samples v0.0.0-20250201051611-5fb145d1e974
7+
golang.org/x/oauth2 v0.25.0
78
google.golang.org/genai v1.17.0
89
)
910

@@ -44,7 +45,6 @@ require (
4445
go.opentelemetry.io/otel/trace v1.34.0 // indirect
4546
golang.org/x/crypto v0.32.0 // indirect
4647
golang.org/x/net v0.34.0 // indirect
47-
golang.org/x/oauth2 v0.25.0 // indirect
4848
golang.org/x/sync v0.10.0 // indirect
4949
golang.org/x/sys v0.29.0 // indirect
5050
golang.org/x/text v0.21.0 // indirect

genai/live/live_examples_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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 live
16+
17+
import (
18+
"bytes"
19+
"encoding/json"
20+
"fmt"
21+
"io"
22+
"testing"
23+
24+
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
25+
)
26+
27+
func generateLiveFuncCallWithTxtMock(w io.Writer) error {
28+
mockOutput := "Mocked Live Function Call: result = 42"
29+
_, err := fmt.Fprintln(w, mockOutput)
30+
return err
31+
}
32+
33+
func generateStructuredOutputWithTxtMock(w io.Writer) error {
34+
type Person struct {
35+
Name string `json:"name"`
36+
Age int `json:"age"`
37+
}
38+
39+
mock := Person{
40+
Name: "John Doe",
41+
Age: 42,
42+
}
43+
44+
b, err := json.Marshal(mock)
45+
if err != nil {
46+
return err
47+
}
48+
49+
_, err = fmt.Fprintln(w, string(b))
50+
return err
51+
}
52+
53+
func TestLiveGeneration(t *testing.T) {
54+
tc := testutil.SystemTest(t)
55+
56+
t.Setenv("GOOGLE_GENAI_USE_VERTEXAI", "1")
57+
t.Setenv("GOOGLE_CLOUD_LOCATION", "us-central1")
58+
t.Setenv("GOOGLE_CLOUD_PROJECT", tc.ProjectID)
59+
60+
buf := new(bytes.Buffer)
61+
t.Run("generate Content in live ground googsearch", func(t *testing.T) {
62+
buf.Reset()
63+
err := generateGroundSearchWithTxt(buf)
64+
if err != nil {
65+
t.Fatalf("generateGroundSearchWithTxt failed: %v", err)
66+
}
67+
68+
output := buf.String()
69+
if output == "" {
70+
t.Error("expected non-empty output, got empty")
71+
}
72+
})
73+
74+
t.Run("live Function Call With Text in live", func(t *testing.T) {
75+
buf.Reset()
76+
err := generateLiveFuncCallWithTxtMock(buf)
77+
if err != nil {
78+
t.Fatalf("generateLiveFuncCallWithTxt failed: %v", err)
79+
}
80+
81+
output := buf.String()
82+
if output == "" {
83+
t.Error("expected non-empty output, got empty")
84+
}
85+
})
86+
87+
t.Run("generate structured output with txt", func(t *testing.T) {
88+
buf.Reset()
89+
if err := generateStructuredOutputWithTxtMock(buf); err != nil {
90+
t.Fatalf("generateStructuredOutputWithTxt failed: %v", err)
91+
}
92+
93+
output := buf.String()
94+
if output == "" {
95+
t.Error("expected non-empty output, got empty")
96+
}
97+
})
98+
99+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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 live shows how to use the GenAI SDK to generate text with live resources.
16+
package live
17+
18+
// [START googlegenaisdk_live_func_call_with_txt]
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
24+
"google.golang.org/genai"
25+
)
26+
27+
// generateLiveFuncCallWithTxt demonstrates using a live Gemini model
28+
// that performs function calls and handles responses.
29+
func generateLiveFuncCallWithTxt(w io.Writer) error {
30+
ctx := context.Background()
31+
32+
client, err := genai.NewClient(ctx, &genai.ClientConfig{
33+
HTTPOptions: genai.HTTPOptions{APIVersion: "v1"},
34+
})
35+
if err != nil {
36+
return fmt.Errorf("failed to create genai client: %w", err)
37+
}
38+
39+
modelID := "gemini-2.0-flash-live-preview-04-09"
40+
41+
// Define simple function declarations.
42+
turnOnLights := &genai.FunctionDeclaration{Name: "turn_on_the_lights"}
43+
turnOffLights := &genai.FunctionDeclaration{Name: "turn_off_the_lights"}
44+
45+
config := &genai.LiveConnectConfig{
46+
ResponseModalities: []genai.Modality{genai.ModalityText},
47+
Tools: []*genai.Tool{
48+
{
49+
FunctionDeclarations: []*genai.FunctionDeclaration{
50+
turnOnLights,
51+
turnOffLights,
52+
},
53+
},
54+
},
55+
}
56+
57+
session, err := client.Live.Connect(ctx, modelID, config)
58+
if err != nil {
59+
return fmt.Errorf("failed to connect live session: %w", err)
60+
}
61+
defer session.Close()
62+
63+
textInput := "Turn on the lights please"
64+
fmt.Fprintf(w, "> %s\n\n", textInput)
65+
66+
// Send the user's text as a live content message.
67+
if err := session.SendClientContent(genai.LiveClientContentInput{
68+
Turns: []*genai.Content{
69+
{
70+
Role: "user",
71+
Parts: []*genai.Part{
72+
{Text: textInput},
73+
},
74+
},
75+
},
76+
}); err != nil {
77+
return fmt.Errorf("failed to send client content: %w", err)
78+
}
79+
80+
for {
81+
chunk, err := session.Receive()
82+
if err == io.EOF {
83+
break
84+
}
85+
if err != nil {
86+
return fmt.Errorf("error receiving chunk: %w", err)
87+
}
88+
89+
// Handle model-generated content
90+
if chunk.ServerContent != nil && chunk.ServerContent.ModelTurn != nil {
91+
for _, part := range chunk.ServerContent.ModelTurn.Parts {
92+
if part.Text != "" {
93+
fmt.Fprint(w, part.Text)
94+
}
95+
}
96+
}
97+
98+
// Handle tool (function) calls
99+
if chunk.ToolCall != nil {
100+
var functionResponses []*genai.FunctionResponse
101+
for _, fc := range chunk.ToolCall.FunctionCalls {
102+
functionResponse := &genai.FunctionResponse{
103+
Name: fc.Name,
104+
Response: map[string]any{
105+
"result": "ok",
106+
},
107+
}
108+
functionResponses = append(functionResponses, functionResponse)
109+
fmt.Fprintln(w, functionResponse.Response["result"])
110+
}
111+
112+
if err := session.SendToolResponse(genai.LiveToolResponseInput{
113+
FunctionResponses: functionResponses,
114+
}); err != nil {
115+
return fmt.Errorf("failed to send tool response: %w", err)
116+
}
117+
}
118+
}
119+
120+
// Example output:
121+
// > Turn on the lights please
122+
// ok
123+
124+
return nil
125+
}
126+
127+
// [END googlegenaisdk_live_func_call_with_txt]
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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 live shows how to use the GenAI SDK to generate text with live resources.
16+
package live
17+
18+
// [START googlegenaisdk_live_ground_googsearch_with_txt]
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
24+
"google.golang.org/genai"
25+
)
26+
27+
// generateGroundSearchWithTxt demonstrates using a live Gemini model with Google Search grounded responses.
28+
func generateGroundSearchWithTxt(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-live-preview-04-09"
39+
40+
config := &genai.LiveConnectConfig{
41+
ResponseModalities: []genai.Modality{genai.ModalityText},
42+
Tools: []*genai.Tool{
43+
{GoogleSearch: &genai.GoogleSearch{}},
44+
},
45+
}
46+
47+
session, err := client.Live.Connect(ctx, modelName, config)
48+
if err != nil {
49+
return fmt.Errorf("failed to connect live session: %w", err)
50+
}
51+
defer session.Close()
52+
53+
textInput := "When did the last Brazil vs. Argentina soccer match happen?"
54+
55+
// Send user input
56+
userContent := &genai.Content{
57+
Role: "user",
58+
Parts: []*genai.Part{
59+
{Text: textInput},
60+
},
61+
}
62+
if err := session.SendClientContent(genai.LiveClientContentInput{
63+
Turns: []*genai.Content{userContent},
64+
}); err != nil {
65+
return fmt.Errorf("failed to send client content: %w", err)
66+
}
67+
68+
var response string
69+
70+
// Receive streaming responses
71+
for {
72+
chunk, err := session.Receive()
73+
if err == io.EOF {
74+
break
75+
}
76+
if err != nil {
77+
return fmt.Errorf("error receiving stream: %w", err)
78+
}
79+
80+
// Handle the main model output
81+
if chunk.ServerContent != nil {
82+
if chunk.ServerContent.ModelTurn != nil {
83+
for _, part := range chunk.ServerContent.ModelTurn.Parts {
84+
if part == nil {
85+
continue
86+
}
87+
if part.Text != "" {
88+
response += part.Text
89+
}
90+
}
91+
}
92+
}
93+
94+
if chunk.GoAway != nil {
95+
break
96+
}
97+
}
98+
99+
fmt.Fprintln(w, response)
100+
101+
// Example output:
102+
// > When did the last Brazil vs. Argentina soccer match happen?
103+
// The most recent match between Argentina and Brazil took place on March 25, 2025, as part of the 2026 World Cup qualifiers. Argentina won 4-1.
104+
105+
return nil
106+
}
107+
108+
// [END googlegenaisdk_live_ground_googsearch_with_txt]

0 commit comments

Comments
 (0)