diff --git a/genai/content_cache/content_cache_test.go b/genai/content_cache/content_cache_test.go new file mode 100644 index 0000000000..1c9253efa1 --- /dev/null +++ b/genai/content_cache/content_cache_test.go @@ -0,0 +1,66 @@ +// 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 content_cache + +import ( + "bytes" + "fmt" + "strings" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestContentCaching(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) + + // 1) Create a content cache. + // The name of the cache resource created in this step will be used in the next test steps. + cacheName, err := createContentCache(buf) + if err != nil { + t.Fatalf("createContentCache: %v", err.Error()) + } + + // 2) Update the expiration time of the content cache. + err = updateContentCache(buf, cacheName) + if err != nil { + t.Errorf("updateContentCache: %v", err.Error()) + } + + // 3) Use cached content with a text prompt. + err = useContentCacheWithTxt(buf, cacheName) + if err != nil { + t.Errorf("useContentCacheWithTxt: %v", err.Error()) + } + + // 4) Delete the content cache. + buf.Reset() + err = deleteContentCache(buf, cacheName) + if err != nil { + t.Errorf("deleteContentCache: %v", err.Error()) + } + + exp := fmt.Sprintf("Deleted cache %q", cacheName) + act := buf.String() + if !strings.Contains(act, exp) { + t.Errorf("deleteContentCache: got %q, want %q", act, exp) + } +} diff --git a/genai/content_cache/contentcache_create_with_txt_gcs_pdf.go b/genai/content_cache/contentcache_create_with_txt_gcs_pdf.go new file mode 100644 index 0000000000..3084e35395 --- /dev/null +++ b/genai/content_cache/contentcache_create_with_txt_gcs_pdf.go @@ -0,0 +1,102 @@ +// 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 content_cache shows examples of using content caching with the GenAI SDK. +package content_cache + +// [START googlegenaisdk_contentcache_create_with_txt_gcs_pdf] +import ( + "context" + "encoding/json" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// createContentCache shows how to create a content cache with an expiration parameter. +func createContentCache(w io.Writer) (string, error) { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1beta1"}, + }) + if err != nil { + return "", fmt.Errorf("failed to create genai client: %w", err) + } + + modelName := "gemini-1.5-pro-002" + + systemInstruction := "You are an expert researcher. You always stick to the facts " + + "in the sources provided, and never make up new facts. " + + "Now look at these research papers, and answer the following questions." + + cacheContents := []*genai.Content{ + { + Parts: []*genai.Part{ + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/pdf/2312.11805v3.pdf", + MIMEType: "application/pdf", + }}, + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf", + MIMEType: "application/pdf", + }}, + }, + Role: "user", + }, + } + config := &genai.CreateCachedContentConfig{ + Contents: cacheContents, + SystemInstruction: &genai.Content{ + Parts: []*genai.Part{ + {Text: systemInstruction}, + }, + }, + DisplayName: "example-cache", + TTL: "86400s", + } + + res, err := client.Caches.Create(ctx, modelName, config) + if err != nil { + return "", fmt.Errorf("failed to create content cache: %w", err) + } + + cachedContent, err := json.MarshalIndent(res, "", " ") + if err != nil { + return "", fmt.Errorf("failed to marshal cache info: %w", err) + } + + // See the documentation: https://pkg.go.dev/google.golang.org/genai#CachedContent + fmt.Fprintln(w, string(cachedContent)) + + // Example response: + // { + // "name": "projects/111111111111/locations/us-central1/cachedContents/1111111111111111111", + // "displayName": "example-cache", + // "model": "projects/111111111111/locations/us-central1/publishers/google/models/gemini-1.5-pro-002", + // "createTime": "2025-02-18T15:05:08.29468Z", + // "updateTime": "2025-02-18T15:05:08.29468Z", + // "expireTime": "2025-02-19T15:05:08.280828Z", + // "usageMetadata": { + // "imageCount": 167, + // "textCount": 153, + // "totalTokenCount": 43125 + // } + // } + + return res.Name, nil +} + +// [END googlegenaisdk_contentcache_create_with_txt_gcs_pdf] diff --git a/genai/content_cache/contentcache_delete.go b/genai/content_cache/contentcache_delete.go new file mode 100644 index 0000000000..be3baa1f9e --- /dev/null +++ b/genai/content_cache/contentcache_delete.go @@ -0,0 +1,51 @@ +// 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 content_cache shows examples of using content caching with the GenAI SDK. +package content_cache + +// [START googlegenaisdk_contentcache_update] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// deleteContentCache shows how to delete content cache. +func deleteContentCache(w io.Writer, cacheName string) error { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1beta1"}, + }) + if err != nil { + return fmt.Errorf("failed to create genai client: %w", err) + } + + _, err = client.Caches.Delete(ctx, cacheName, &genai.DeleteCachedContentConfig{}) + if err != nil { + return fmt.Errorf("failed to delete content cache: %w", err) + } + + fmt.Fprintf(w, "Deleted cache %q\n", cacheName) + + // Example response: + // Deleted cache "projects/111111111111/locations/us-central1/cachedContents/1111111111111111111" + + return nil +} + +// [END googlegenaisdk_contentcache_update] diff --git a/genai/content_cache/contentcache_update.go b/genai/content_cache/contentcache_update.go new file mode 100644 index 0000000000..7a6a1feef0 --- /dev/null +++ b/genai/content_cache/contentcache_update.go @@ -0,0 +1,67 @@ +// 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 content_cache shows examples of using content caching with the GenAI SDK. +package content_cache + +// [START googlegenaisdk_contentcache_update] +import ( + "context" + "fmt" + "io" + "time" + + genai "google.golang.org/genai" +) + +// updateContentCache shows how to update content cache expiration time. +func updateContentCache(w io.Writer, cacheName string) error { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1beta1"}, + }) + if err != nil { + return fmt.Errorf("failed to create genai client: %w", err) + } + + // Update expire time using TTL + resp, err := client.Caches.Update(ctx, cacheName, &genai.UpdateCachedContentConfig{ + TTL: "36000s", + }) + if err != nil { + return fmt.Errorf("failed to update content cache exp. time with TTL: %w", err) + } + + fmt.Fprintf(w, "Cache expires in: %s\n", time.Until(*resp.ExpireTime)) + // Example response: + // Cache expires in: 10h0m0.005875s + + // Update expire time using specific time stamp + inSevenDays := time.Now().Add(7 * 24 * time.Hour) + resp, err = client.Caches.Update(ctx, cacheName, &genai.UpdateCachedContentConfig{ + ExpireTime: &inSevenDays, + }) + if err != nil { + return fmt.Errorf("failed to update content cache expire time: %w", err) + } + + fmt.Fprintf(w, "Cache expires in: %s\n", time.Until(*resp.ExpireTime)) + // Example response: + // Cache expires in: 167h59m59.80327s + + return nil +} + +// [END googlegenaisdk_contentcache_update] diff --git a/genai/content_cache/contentcache_use_with_txt.go b/genai/content_cache/contentcache_use_with_txt.go new file mode 100644 index 0000000000..331eab1553 --- /dev/null +++ b/genai/content_cache/contentcache_use_with_txt.go @@ -0,0 +1,65 @@ +// 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 content_cache shows examples of using content caching with the GenAI SDK. +package content_cache + +// [START googlegenaisdk_contentcache_use_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// useContentCacheWithTxt shows how to use content cache to generate text content. +func useContentCacheWithTxt(w io.Writer, cacheName string) error { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1beta1"}, + }) + if err != nil { + return fmt.Errorf("failed to create genai client: %w", err) + } + + resp, err := client.Models.GenerateContent(ctx, + "gemini-1.5-pro-002", + genai.Text("Summarize the pdfs"), + &genai.GenerateContentConfig{ + CachedContent: cacheName, + }, + ) + if err != nil { + return fmt.Errorf("failed to use content cache to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // The provided research paper introduces Gemini 1.5 Pro, a multimodal model capable of recalling + // and reasoning over information from very long contexts (up to 10 million tokens). Key findings include: + // + // * **Long Context Performance:** + // ... + + return nil +} + +// [END googlegenaisdk_contentcache_use_with_txt] diff --git a/genai/controlled_generation/controlled_generation_examples_test.go b/genai/controlled_generation/controlled_generation_examples_test.go new file mode 100644 index 0000000000..311e86129f --- /dev/null +++ b/genai/controlled_generation/controlled_generation_examples_test.go @@ -0,0 +1,71 @@ +// 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 controlled_generation + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestTextGeneration(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 with enum schema", func(t *testing.T) { + buf.Reset() + err := generateWithEnumSchema(buf) + if err != nil { + t.Fatalf("generateWithEnumSchema failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with response schema", func(t *testing.T) { + buf.Reset() + err := generateWithRespSchema(buf) + if err != nil { + t.Fatalf("generateWithRespSchema failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with response schema with nullable values", func(t *testing.T) { + buf.Reset() + err := generateWithNullables(buf) + if err != nil { + t.Fatalf("generateWithNullables failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) +} diff --git a/genai/controlled_generation/ctrlgen_with_enum_schema.go b/genai/controlled_generation/ctrlgen_with_enum_schema.go new file mode 100644 index 0000000000..f61e3ef627 --- /dev/null +++ b/genai/controlled_generation/ctrlgen_with_enum_schema.go @@ -0,0 +1,69 @@ +// 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_enum_schema] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithEnumSchema shows how to use enum schema to generate output. +func generateWithEnumSchema(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What type of instrument is an oboe?"}, + }}, + } + config := &genai.GenerateContentConfig{ + ResponseMIMEType: "text/x.enum", + ResponseSchema: &genai.Schema{ + Type: "STRING", + Enum: []string{"Percussion", "String", "Woodwind", "Brass", "Keyboard"}, + }, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Woodwind + + return nil +} + +// [END googlegenaisdk_ctrlgen_with_enum_schema] diff --git a/genai/controlled_generation/ctrlgen_with_nullable_schema.go b/genai/controlled_generation/ctrlgen_with_nullable_schema.go new file mode 100644 index 0000000000..a5166f7ff3 --- /dev/null +++ b/genai/controlled_generation/ctrlgen_with_nullable_schema.go @@ -0,0 +1,106 @@ +// 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_nullable_schema] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithNullables shows how to use the response schema with nullable values. +func generateWithNullables(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.0-flash-001" + prompt := ` +The week ahead brings a mix of weather conditions. +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. +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. +Tuesday brings rain showers, with temperatures dropping to 64°F and humidity rising to 70%. +Wednesday may see thunderstorms, with a temperature of 68°F. +Thursday will be cloudy with a temperature of 66°F and moderate humidity at 60%. +Friday returns to partly cloudy conditions, with a temperature of 73°F and the Winds will be light at 12 km/h. +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. +` + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: prompt}, + }}, + } + config := &genai.GenerateContentConfig{ + ResponseMIMEType: "application/json", + // See the OpenAPI specification for more details and examples: + // https://spec.openapis.org/oas/v3.0.3.html#schema-object + ResponseSchema: &genai.Schema{ + Type: "object", + Properties: map[string]*genai.Schema{ + "forecast": { + Type: "array", + Items: &genai.Schema{ + Type: "object", + Properties: map[string]*genai.Schema{ + "Day": {Type: "string", Nullable: true}, + "Forecast": {Type: "string", Nullable: true}, + "Temperature": {Type: "integer", Nullable: true}, + "Humidity": {Type: "string", Nullable: true}, + "Wind Speed": {Type: "integer", Nullable: true}, + }, + Required: []string{"Day", "Temperature", "Forecast", "Wind Speed"}, + }, + }, + }, + }, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // 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 nil +} + +// [END googlegenaisdk_ctrlgen_with_nullable_schema] diff --git a/genai/controlled_generation/ctrlgen_with_resp_schema.go b/genai/controlled_generation/ctrlgen_with_resp_schema.go new file mode 100644 index 0000000000..bfef007ee3 --- /dev/null +++ b/genai/controlled_generation/ctrlgen_with_resp_schema.go @@ -0,0 +1,94 @@ +// 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_resp_schema] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithRespSchema shows how to use a response schema to generate output in a specific format. +func generateWithRespSchema(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) + } + + config := &genai.GenerateContentConfig{ + ResponseMIMEType: "application/json", + // See the OpenAPI specification for more details and examples: + // https://spec.openapis.org/oas/v3.0.3.html#schema-object + ResponseSchema: &genai.Schema{ + Type: "array", + Items: &genai.Schema{ + Type: "object", + Properties: map[string]*genai.Schema{ + "recipe_name": {Type: "string"}, + "ingredients": { + Type: "array", + Items: &genai.Schema{Type: "string"}, + }, + }, + Required: []string{"recipe_name", "ingredients"}, + }, + }, + } + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "List a few popular cookie recipes."}, + }}, + } + modelName := "gemini-2.0-flash-001" + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // [ + // { + // "ingredients": [ + // "2 1/4 cups all-purpose flour", + // "1 teaspoon baking soda", + // ... + // ], + // "recipe_name": "Chocolate Chip Cookies" + // }, + // { + // ... + // }, + // ... + // ] + + return nil +} + +// [END googlegenaisdk_ctrlgen_with_resp_schema] diff --git a/genai/count_tokens/count_tokens_examples_test.go b/genai/count_tokens/count_tokens_examples_test.go new file mode 100644 index 0000000000..22da59959a --- /dev/null +++ b/genai/count_tokens/count_tokens_examples_test.go @@ -0,0 +1,84 @@ +// 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 count_tokens + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestTextGeneration(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 with text prompt", func(t *testing.T) { + buf.Reset() + err := generateTextAndCount(buf) + if err != nil { + t.Fatalf("generateTextAndCount failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("count tokens with text input", func(t *testing.T) { + buf.Reset() + err := countWithTxt(buf) + if err != nil { + t.Fatalf("countWithTxt failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("count tokens with text and video inputs", func(t *testing.T) { + buf.Reset() + err := countWithTxtAndVid(buf) + if err != nil { + t.Fatalf("countWithTxtAndVid failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("compute tokens with text input", func(t *testing.T) { + buf.Reset() + err := computeWithTxt(buf) + if err != nil { + t.Fatalf("computeWithTxt failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) +} diff --git a/genai/count_tokens/counttoken_compute_with_txt.go b/genai/count_tokens/counttoken_compute_with_txt.go new file mode 100644 index 0000000000..f684d7afb4 --- /dev/null +++ b/genai/count_tokens/counttoken_compute_with_txt.go @@ -0,0 +1,91 @@ +// 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 count_tokens shows examples of counting tokens using the GenAI SDK. +package count_tokens + +// [START googlegenaisdk_counttoken_compute_with_txt] +import ( + "context" + "encoding/json" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// computeWithTxt shows how to compute tokens with text input. +func computeWithTxt(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What's the longest word in the English language?"}, + }}, + } + + resp, err := client.Models.ComputeTokens(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + type tokenInfoDisplay struct { + IDs []int64 `json:"token_ids"` + Tokens []string `json:"tokens"` + } + // See the documentation: https://pkg.go.dev/google.golang.org/genai#ComputeTokensResponse + for _, instance := range resp.TokensInfo { + display := tokenInfoDisplay{ + IDs: instance.TokenIDs, + Tokens: make([]string, len(instance.Tokens)), + } + for i, t := range instance.Tokens { + display.Tokens[i] = string(t) + } + + data, err := json.MarshalIndent(display, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal token info: %w", err) + } + fmt.Fprintln(w, string(data)) + } + + // Example response: + // { + // "ids": [ + // 1841, + // 235303, + // 235256, + // ... + // ], + // "values": [ + // "What", + // "'", + // "s", + // ... + // ] + // } + + return nil +} + +// [END googlegenaisdk_counttoken_compute_with_txt] diff --git a/genai/count_tokens/counttoken_resp_with_txt.go b/genai/count_tokens/counttoken_resp_with_txt.go new file mode 100644 index 0000000000..b835d8d448 --- /dev/null +++ b/genai/count_tokens/counttoken_resp_with_txt.go @@ -0,0 +1,67 @@ +// 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 count_tokens shows examples of counting tokens using the GenAI SDK. +package count_tokens + +// [START googlegenaisdk_counttoken_resp_with_txt] +import ( + "context" + "encoding/json" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateTextAndCount shows how to generate text and obtain token count metadata from the model response. +func generateTextAndCount(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Why is the sky blue?"}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + usage, err := json.MarshalIndent(resp.UsageMetadata, "", " ") + if err != nil { + return fmt.Errorf("failed to convert usage metadata to JSON: %w", err) + } + fmt.Fprintln(w, string(usage)) + + // Example response: + // { + // "candidatesTokenCount": 339, + // "promptTokenCount": 6, + // "totalTokenCount": 345 + // } + + return nil +} + +// [END googlegenaisdk_counttoken_resp_with_txt] diff --git a/genai/count_tokens/counttoken_with_txt.go b/genai/count_tokens/counttoken_with_txt.go new file mode 100644 index 0000000000..8a0e1952a0 --- /dev/null +++ b/genai/count_tokens/counttoken_with_txt.go @@ -0,0 +1,59 @@ +// 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 count_tokens shows examples of counting tokens using the GenAI SDK. +package count_tokens + +// [START googlegenaisdk_counttoken_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// countWithTxt shows how to count tokens with text input. +func countWithTxt(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What's the highest mountain in Africa?"}, + }}, + } + + resp, err := client.Models.CountTokens(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + fmt.Fprintf(w, "Total: %d\nCached: %d\n", resp.TotalTokens, resp.CachedContentTokenCount) + + // Example response: + // Total: 9 + // Cached: 0 + + return nil +} + +// [END googlegenaisdk_counttoken_with_txt] diff --git a/genai/count_tokens/counttoken_with_txt_vid.go b/genai/count_tokens/counttoken_with_txt_vid.go new file mode 100644 index 0000000000..8bd0a5469c --- /dev/null +++ b/genai/count_tokens/counttoken_with_txt_vid.go @@ -0,0 +1,63 @@ +// 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 count_tokens shows examples of counting tokens using the GenAI SDK. +package count_tokens + +// [START googlegenaisdk_counttoken_with_txt_vid] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// countWithTxtAndVid shows how to count tokens with text and video inputs. +func countWithTxtAndVid(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Provide a description of the video."}, + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/video/pixel8.mp4", + MIMEType: "video/mp4", + }}, + }}, + } + + resp, err := client.Models.CountTokens(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + fmt.Fprintf(w, "Total: %d\nCached: %d\n", resp.TotalTokens, resp.CachedContentTokenCount) + + // Example response: + // Total: 16252 + // Cached: 0 + + return nil +} + +// [END googlegenaisdk_counttoken_with_txt_vid] diff --git a/genai/go.mod b/genai/go.mod index feb4a76b5e..a2373bf1d6 100644 --- a/genai/go.mod +++ b/genai/go.mod @@ -4,7 +4,7 @@ go 1.23.0 require ( github.com/GoogleCloudPlatform/golang-samples v0.0.0-20250201051611-5fb145d1e974 - google.golang.org/genai v0.3.0 + google.golang.org/genai v0.4.0 ) require ( diff --git a/genai/go.sum b/genai/go.sum index 0d6ca71543..c1d6f9a863 100644 --- a/genai/go.sum +++ b/genai/go.sum @@ -113,8 +113,8 @@ golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= google.golang.org/api v0.217.0 h1:GYrUtD289o4zl1AhiTZL0jvQGa2RDLyC+kX1N/lfGOU= google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI= -google.golang.org/genai v0.3.0 h1:xSYQAFmZvHbQhK8Ay9FvpecMcqVhTGZbLSRiDyxEVBs= -google.golang.org/genai v0.3.0/go.mod h1:yPyKKBezIg2rqZziLhHQ5CD62HWr7sLDLc2PDzdrNVs= +google.golang.org/genai v0.4.0 h1:nHLpFvp1i2nUGQ8CjIQ8j/6d3H79Echt1jiNLb9myDk= +google.golang.org/genai v0.4.0/go.mod h1:yPyKKBezIg2rqZziLhHQ5CD62HWr7sLDLc2PDzdrNVs= google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f h1:387Y+JbxF52bmesc8kq1NyYIp33dnxCw6eiA7JMsTmw= google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:0joYwWwLQh18AOj8zMYeZLjzuqcYTU3/nC5JdCvC3JI= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= diff --git a/genai/text_generation/testdata/describe_video_content.mp4 b/genai/text_generation/testdata/describe_video_content.mp4 new file mode 100644 index 0000000000..93176ae76f Binary files /dev/null and b/genai/text_generation/testdata/describe_video_content.mp4 differ diff --git a/genai/text_generation/testdata/latte.jpg b/genai/text_generation/testdata/latte.jpg new file mode 100644 index 0000000000..e942ca6230 Binary files /dev/null and b/genai/text_generation/testdata/latte.jpg differ diff --git a/genai/text_generation/testdata/scones.jpg b/genai/text_generation/testdata/scones.jpg new file mode 100644 index 0000000000..b5ee1b0707 Binary files /dev/null and b/genai/text_generation/testdata/scones.jpg differ diff --git a/genai/text_generation/text_generation_examples_test.go b/genai/text_generation/text_generation_examples_test.go new file mode 100644 index 0000000000..942f91e2f7 --- /dev/null +++ b/genai/text_generation/text_generation_examples_test.go @@ -0,0 +1,214 @@ +// 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 text_generation + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestTextGeneration(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 with text prompt", func(t *testing.T) { + buf.Reset() + err := generateWithText(buf) + if err != nil { + t.Fatalf("generateWithText failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with text prompt and custom configuration", func(t *testing.T) { + buf.Reset() + err := generateWithConfig(buf) + if err != nil { + t.Fatalf("generateWithConfig failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with text prompt and system instructions", func(t *testing.T) { + buf.Reset() + err := generateWithSystem(buf) + if err != nil { + t.Fatalf("generateWithSystem failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate stream with text prompt", func(t *testing.T) { + buf.Reset() + err := generateWithTextStream(buf) + if err != nil { + t.Fatalf("generateWithTextStream failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with text and image prompt", func(t *testing.T) { + buf.Reset() + err := generateWithTextImage(buf) + if err != nil { + t.Fatalf("generateWithTextImage failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with multiple image inputs", func(t *testing.T) { + buf.Reset() + err := generateWithMultiImg(buf) + if err != nil { + t.Fatalf("generateWithMultiImg failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with multiple local image inputs", func(t *testing.T) { + buf.Reset() + err := generateWithMultiLocalImg(buf) + if err != nil { + t.Fatalf("generateWithMultiLocalImg failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with pdf file input", func(t *testing.T) { + buf.Reset() + err := generateWithPDF(buf) + if err != nil { + t.Fatalf("generateWithPDF failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with video file input (no sound)", func(t *testing.T) { + buf.Reset() + err := generateWithMuteVideo(buf) + if err != nil { + t.Fatalf("generateWithMuteVideo failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with video file input", func(t *testing.T) { + buf.Reset() + err := generateWithVideo(buf) + if err != nil { + t.Fatalf("generateWithVideo failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with audio file input", func(t *testing.T) { + buf.Reset() + err := generateWithAudio(buf) + if err != nil { + t.Fatalf("generateWithAudio failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate an audio transcript", func(t *testing.T) { + buf.Reset() + err := generateAudioTranscript(buf) + if err != nil { + t.Fatalf("generateAudioTranscript failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with YT video file input", func(t *testing.T) { + buf.Reset() + err := generateWithYTVideo(buf) + if err != nil { + t.Fatalf("generateWithYTVideo failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with local video input", func(t *testing.T) { + buf.Reset() + err := generateWithLocalVideo(buf) + if err != nil { + t.Fatalf("generateWithLocalVideo failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) +} diff --git a/genai/text_generation/textgen_code_with_local_video.go b/genai/text_generation/textgen_code_with_local_video.go new file mode 100644 index 0000000000..5746f44166 --- /dev/null +++ b/genai/text_generation/textgen_code_with_local_video.go @@ -0,0 +1,77 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_code_with_local_video] +import ( + "context" + "fmt" + "io" + "os" + + genai "google.golang.org/genai" +) + +// generateWithLocalVideo shows how to generate text using a local video file as input. +func generateWithLocalVideo(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) + } + + // TODO(Developer): Update the path to file (video source: + // https://storage.googleapis.com/cloud-samples-data/generative-ai/video/describe_video_content.mp4) + videoBytes, err := os.ReadFile("./testdata/describe_video_content.mp4") + if err != nil { + return fmt.Errorf("failed to read video file: %w", err) + } + + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Write a short and engaging blog post based on this video."}, + {InlineData: &genai.Blob{ + Data: videoBytes, + MIMEType: "video/mp4", + }}, + }}, + } + modelName := "gemini-2.0-flash-001" + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Okay, here's a blog post inspired by the provided image: + // + // **Title: Conquer Your Fears, Conquer the Wall!** + // ... + + return nil +} + +// [END googlegenaisdk_textgen_code_with_local_video] diff --git a/genai/text_generation/textgen_code_with_pdf.go b/genai/text_generation/textgen_code_with_pdf.go new file mode 100644 index 0000000000..ded73de243 --- /dev/null +++ b/genai/text_generation/textgen_code_with_pdf.go @@ -0,0 +1,68 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_code_with_pdf] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithPDF shows how to generate code using a PDF file input. +func generateWithPDF(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Convert this python code to use Google Python Style Guide."}, + {FileData: &genai.FileData{ + FileURI: "https://storage.googleapis.com/cloud-samples-data/generative-ai/text/inefficient_fibonacci_series_python_code.pdf", + MIMEType: "application/pdf", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // ```python + // def fibonacci(n: int) -> list[int]: + // ... + + return nil +} + +// [END googlegenaisdk_textgen_code_with_pdf] diff --git a/genai/text_generation/textgen_config_with_txt.go b/genai/text_generation/textgen_config_with_txt.go new file mode 100644 index 0000000000..04e10cdf18 --- /dev/null +++ b/genai/text_generation/textgen_config_with_txt.go @@ -0,0 +1,65 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_config_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithConfig shows how to generate text using a text prompt and custom configuration. +func generateWithConfig(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.0-flash-001" + contents := genai.Text("Why is the sky blue?") + // See the documentation: https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateContentConfig + config := &genai.GenerateContentConfig{ + Temperature: genai.Ptr(0.0), + CandidateCount: genai.Ptr(int64(1)), + ResponseMIMEType: "application/json", + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + // Example response: + // { + // "explanation": "The sky is blue due to a phenomenon called Rayleigh scattering ... + // } + + return nil +} + +// [END googlegenaisdk_textgen_config_with_txt] diff --git a/genai/text_generation/textgen_sys_instr_with_txt.go b/genai/text_generation/textgen_sys_instr_with_txt.go new file mode 100644 index 0000000000..69a0583560 --- /dev/null +++ b/genai/text_generation/textgen_sys_instr_with_txt.go @@ -0,0 +1,65 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_sys_instr_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithSystem shows how to generate text using a text prompt and system instruction. +func generateWithSystem(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.0-flash-001" + contents := genai.Text("Why is the sky blue?") + config := &genai.GenerateContentConfig{ + SystemInstruction: &genai.Content{ + Parts: []*genai.Part{ + {Text: "You're a language translator. Your mission is to translate text in English to French."}, + }, + }, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Pourquoi le ciel est-il bleu ? + + return nil +} + +// [END googlegenaisdk_textgen_sys_instr_with_txt] diff --git a/genai/text_generation/textgen_transcript_with_gcs_audio.go b/genai/text_generation/textgen_transcript_with_gcs_audio.go new file mode 100644 index 0000000000..8b9f89aa11 --- /dev/null +++ b/genai/text_generation/textgen_transcript_with_gcs_audio.go @@ -0,0 +1,69 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_transcript_with_gcs_audio] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateAudioTranscript shows how to generate an audio transcript. +func generateAudioTranscript(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: `Transcribe the interview, in the format of timecode, speaker, caption. +Use speaker A, speaker B, etc. to identify speakers.`}, + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/audio/pixel.mp3", + MIMEType: "audio/mpeg", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // 00:00:00, A: your devices are getting better over time. + // 00:01:13, A: And so we think about it across the entire portfolio from phones to watch, ... + // ... + + return nil +} + +// [END googlegenaisdk_textgen_transcript_with_gcs_audio] diff --git a/genai/text_generation/textgen_with_gcs_audio.go b/genai/text_generation/textgen_with_gcs_audio.go new file mode 100644 index 0000000000..8b5dbc7d10 --- /dev/null +++ b/genai/text_generation/textgen_with_gcs_audio.go @@ -0,0 +1,77 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_gcs_audio] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithAudio shows how to generate text using an audio input. +func generateWithAudio(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: `Provide the summary of the audio file. +Summarize the main points of the audio concisely. +Create a chapter breakdown with timestamps for key sections or topics discussed.`}, + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/audio/pixel.mp3", + MIMEType: "audio/mpeg", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Here is a summary and chapter breakdown of the audio file: + // + // **Summary:** + // + // The audio file is a "Made by Google" podcast episode discussing the Pixel Feature Drops, ... + // + // **Chapter Breakdown:** + // + // * **0:00 - 0:54:** Introduction to the podcast and guests, Aisha Sharif and DeCarlos Love. + // ... + + return nil +} + +// [END googlegenaisdk_textgen_with_gcs_audio] diff --git a/genai/text_generation/textgen_with_multi_img.go b/genai/text_generation/textgen_with_multi_img.go new file mode 100644 index 0000000000..7928bbe5e8 --- /dev/null +++ b/genai/text_generation/textgen_with_multi_img.go @@ -0,0 +1,82 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_multi_img] +import ( + "context" + "fmt" + "io" + "os" + + genai "google.golang.org/genai" +) + +// generateWithMultiImg shows how to generate text using multiple image inputs. +func generateWithMultiImg(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) + } + + // TODO(Developer): Update the path to file (image source: + // https://storage.googleapis.com/cloud-samples-data/generative-ai/image/latte.jpg ) + imageBytes, err := os.ReadFile("./testdata/latte.jpg") + if err != nil { + return fmt.Errorf("failed to read image: %w", err) + } + + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Write an advertising jingle based on the items in both images."}, + {FileData: &genai.FileData{ + // Image source: https://storage.googleapis.com/cloud-samples-data/generative-ai/image/scones.jpg + FileURI: "gs://cloud-samples-data/generative-ai/image/scones.jpg", + MIMEType: "image/jpeg", + }}, + {InlineData: &genai.Blob{ + Data: imageBytes, + MIMEType: "image/jpeg", + }}, + }}, + } + modelName := "gemini-2.0-flash-001" + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Okay, here's an advertising jingle inspired by the blueberry scones, coffee, flowers, chocolate cake, and latte: + // + // (Upbeat, jazzy music) + // ... + + return nil +} + +// [END googlegenaisdk_textgen_with_multi_img] diff --git a/genai/text_generation/textgen_with_multi_local_img.go b/genai/text_generation/textgen_with_multi_local_img.go new file mode 100644 index 0000000000..be78d9d650 --- /dev/null +++ b/genai/text_generation/textgen_with_multi_local_img.go @@ -0,0 +1,84 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_multi_local_img] +import ( + "context" + "fmt" + "io" + "os" + + genai "google.golang.org/genai" +) + +// generateWithMultiLocalImg shows how to generate text using multiple local images as inputs. +func generateWithMultiLocalImg(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) + } + + // TODO(Developer): Update with paths to your image files. + imageBytes1, err := os.ReadFile("./testdata/latte.jpg") + if err != nil { + return fmt.Errorf("failed to read first image: %w", err) + } + imageBytes2, err := os.ReadFile("./testdata/scones.jpg") + if err != nil { + return fmt.Errorf("failed to read first image: %w", err) + } + + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Write an advertising jingle based on the items in both images."}, + {InlineData: &genai.Blob{ + Data: imageBytes1, + MIMEType: "image/jpeg", + }}, + {InlineData: &genai.Blob{ + Data: imageBytes2, + MIMEType: "image/jpeg", + }}, + }}, + } + modelName := "gemini-2.0-flash-001" + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Okay, here's a jingle inspired by the images of cake, coffee, and blueberry scones: + // + // (Upbeat, folksy music) + // ... + + return nil +} + +// [END googlegenaisdk_textgen_with_multi_local_img] diff --git a/genai/text_generation/textgen_with_mute_video.go b/genai/text_generation/textgen_with_mute_video.go new file mode 100644 index 0000000000..1591907469 --- /dev/null +++ b/genai/text_generation/textgen_with_mute_video.go @@ -0,0 +1,66 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_mute_video] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithMuteVideo shows how to generate text using a video with no sound as the input. +func generateWithMuteVideo(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What is in the video?"}, + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/video/ad_copy_from_video.mp4", + MIMEType: "video/mp4", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // The video shows several surfers riding waves in an ocean setting. The waves are ... + + return nil +} + +// [END googlegenaisdk_textgen_with_mute_video] diff --git a/genai/text_generation/textgen_with_txt_img.go b/genai/text_generation/textgen_with_txt_img.go new file mode 100644 index 0000000000..ed58832b02 --- /dev/null +++ b/genai/text_generation/textgen_with_txt_img.go @@ -0,0 +1,67 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_txt_img] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithTextImage shows how to generate text using both text and image input +func generateWithTextImage(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What is shown in this image?"}, + {FileData: &genai.FileData{ + // Image source: https://storage.googleapis.com/cloud-samples-data/generative-ai/image/scones.jpg + FileURI: "gs://cloud-samples-data/generative-ai/image/scones.jpg", + MIMEType: "image/jpeg", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // The image shows an overhead shot of a rustic, artistic arrangement on a surface that ... + + return nil +} + +// [END googlegenaisdk_textgen_with_txt_img] diff --git a/genai/text_generation/textgen_with_txt_stream.go b/genai/text_generation/textgen_with_txt_stream.go new file mode 100644 index 0000000000..59efe88be2 --- /dev/null +++ b/genai/text_generation/textgen_with_txt_stream.go @@ -0,0 +1,62 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_txt_stream] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithTextStream shows how to generate text stream using a text prompt. +func generateWithTextStream(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.0-flash-001" + contents := genai.Text("Why is the sky blue?") + + for resp, err := range client.Models.GenerateContentStream(ctx, modelName, contents, nil) { + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + chunk, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, chunk) + } + + // Example response: + // The + // sky is blue + // because of a phenomenon called **Rayleigh scattering**. Here's the breakdown: + // ... + + return nil +} + +// [END googlegenaisdk_textgen_with_txt_stream] diff --git a/genai/text_generation/textgen_with_video.go b/genai/text_generation/textgen_with_video.go new file mode 100644 index 0000000000..352431e38f --- /dev/null +++ b/genai/text_generation/textgen_with_video.go @@ -0,0 +1,77 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_video] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithVideo shows how to generate text using a video input. +func generateWithVideo(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: `Analyze the provided video file, including its audio. +Summarize the main points of the video concisely. +Create a chapter breakdown with timestamps for key sections or topics discussed.`}, + {FileData: &genai.FileData{ + FileURI: "gs://cloud-samples-data/generative-ai/video/pixel8.mp4", + MIMEType: "video/mp4", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Here's an analysis of the provided video file: + // + // **Summary** + // + // The video features Saeka Shimada, a photographer in Tokyo, who uses the new Pixel phone ... + // + // **Chapter Breakdown** + // + // * **0:00-0:05**: Introduction to Saeka Shimada and her work as a photographer in Tokyo. + // ... + + return nil +} + +// [END googlegenaisdk_textgen_with_video] diff --git a/genai/text_generation/textgen_with_youtube_video.go b/genai/text_generation/textgen_with_youtube_video.go new file mode 100644 index 0000000000..b7312e3506 --- /dev/null +++ b/genai/text_generation/textgen_with_youtube_video.go @@ -0,0 +1,69 @@ +// 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 text_generation shows examples of generating text using the GenAI SDK. +package text_generation + +// [START googlegenaisdk_textgen_with_youtube_video] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithYTVideo shows how to generate text using a YouTube video as input. +func generateWithYTVideo(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "Write a short and engaging blog post based on this video."}, + {FileData: &genai.FileData{ + FileURI: "https://www.youtube.com/watch?v=3KtWfp0UopM", + MIMEType: "video/mp4", + }}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, nil) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // Okay, here’s a short and engaging blog post based on the provided video. + // + // **Google's 25th: A Look Back at What We've Searched** + // ... + + return nil +} + +// [END googlegenaisdk_textgen_with_youtube_video] diff --git a/genai/tools/testdata/640px-Monty_open_door.svg.png b/genai/tools/testdata/640px-Monty_open_door.svg.png new file mode 100644 index 0000000000..90f83375e3 Binary files /dev/null and b/genai/tools/testdata/640px-Monty_open_door.svg.png differ diff --git a/genai/tools/tools_code_exec_with_txt.go b/genai/tools/tools_code_exec_with_txt.go new file mode 100644 index 0000000000..6d5f7ed879 --- /dev/null +++ b/genai/tools/tools_code_exec_with_txt.go @@ -0,0 +1,89 @@ +// 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 tools shows examples of various tools that Gemini model can use to generate text. +package tools + +// [START googlegenaisdk_tools_code_exec_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithCodeExec shows how to generate text using the code execution tool. +func generateWithCodeExec(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) + } + + prompt := "Calculate 20th fibonacci number. Then find the nearest palindrome to it." + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: prompt}, + }}, + } + config := &genai.GenerateContentConfig{ + Tools: []*genai.Tool{ + {CodeExecution: &genai.ToolCodeExecution{}}, + }, + Temperature: genai.Ptr(0.0), + } + modelName := "gemini-2.0-flash-001" + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + for _, p := range resp.Candidates[0].Content.Parts { + if p.Text != "" { + fmt.Fprintf(w, "Gemini: %s", p.Text) + } + if p.ExecutableCode != nil { + fmt.Fprintf(w, "Language: %s\n%s\n", p.ExecutableCode.Language, p.ExecutableCode.Code) + } + if p.CodeExecutionResult != nil { + fmt.Fprintf(w, "Outcome: %s\n%s\n", p.CodeExecutionResult.Outcome, p.CodeExecutionResult.Output) + } + } + + // Example response: + // Gemini: Okay, I can do that. First, I'll calculate the 20th Fibonacci number. Then, I need ... + // + // Language: PYTHON + // + // def fibonacci(n): + // ... + // + // fib_20 = fibonacci(20) + // print(f'{fib_20=}') + // + // Outcome: OUTCOME_OK + // fib_20=6765 + // + // Now that I have the 20th Fibonacci number (6765), I need to find the nearest palindrome. ... + // ... + + return nil +} + +// [END googlegenaisdk_tools_code_exec_with_txt] diff --git a/genai/tools/tools_code_exec_with_txt_local_img.go b/genai/tools/tools_code_exec_with_txt_local_img.go new file mode 100644 index 0000000000..0ca6a697ab --- /dev/null +++ b/genai/tools/tools_code_exec_with_txt_local_img.go @@ -0,0 +1,111 @@ +// 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 tools shows examples of various tools that Gemini model can use to generate text. +package tools + +// [START googlegenaisdk_tools_code_exec_with_txt_local_img] +import ( + "context" + "fmt" + "io" + "os" + + genai "google.golang.org/genai" +) + +// generateWithCodeExecAndImg shows how to generate text using the code execution tool and a local image. +func generateWithCodeExecAndImg(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) + } + + // Image source: + // https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Monty_open_door.svg/640px-Monty_open_door.svg.png + imgBytes, err := os.ReadFile("./testdata/640px-Monty_open_door.svg.png") + if err != nil { + return fmt.Errorf("failed to read input file: %w", err) + } + + prompt := ` +Run a simulation of the Monty Hall Problem with 1,000 trials. +Here's how this works as a reminder. In the Monty Hall Problem, you're on a game +show with three doors. Behind one is a car, and behind the others are goats. You +pick a door. The host, who knows what's behind the doors, opens a different door +to reveal a goat. Should you switch to the remaining unopened door? +The answer has always been a little difficult for me to understand when people +solve it with math - so please run a simulation with Python to show me what the +best strategy is. +Thank you!` + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: prompt}, + {InlineData: &genai.Blob{ + Data: imgBytes, + MIMEType: "image/png", + }}, + }}, + } + config := &genai.GenerateContentConfig{ + Tools: []*genai.Tool{ + {CodeExecution: &genai.ToolCodeExecution{}}, + }, + Temperature: genai.Ptr(0.0), + } + modelName := "gemini-2.0-flash-001" + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + for _, p := range resp.Candidates[0].Content.Parts { + if p.Text != "" { + fmt.Fprintf(w, "Gemini: %s", p.Text) + } + if p.ExecutableCode != nil { + fmt.Fprintf(w, "Language: %s\n%s\n", p.ExecutableCode.Language, p.ExecutableCode.Code) + } + if p.CodeExecutionResult != nil { + fmt.Fprintf(w, "Outcome: %s\n%s\n", p.CodeExecutionResult.Outcome, p.CodeExecutionResult.Output) + } + } + + // Example response: + // Language: PYTHON + // + // import random + // + // def monty_hall_simulation(num_trials): + // ... + // + // # Run the simulation for 1000 trials + // num_trials = 1000 + // switch_win_percentage, stay_win_percentage = monty_hall_simulation(num_trials) + // ... + // Outcome: OUTCOME_OK + // Switching Doors Win Percentage: 63.80% + // Staying with Original Door Win Percentage: 36.20% + // + // Gemini: The results of the simulation clearly demonstrate that switching doors is the better strategy. ... + + return nil +} + +// [END googlegenaisdk_tools_code_exec_with_txt_local_img] diff --git a/genai/tools/tools_examples_test.go b/genai/tools/tools_examples_test.go new file mode 100644 index 0000000000..92d9545132 --- /dev/null +++ b/genai/tools/tools_examples_test.go @@ -0,0 +1,84 @@ +// 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 tools + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestTextGeneration(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 with code execution tool", func(t *testing.T) { + buf.Reset() + err := generateWithCodeExec(buf) + if err != nil { + t.Fatalf("generateWithCodeExec failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with code execution tool and local image input", func(t *testing.T) { + buf.Reset() + err := generateWithCodeExecAndImg(buf) + if err != nil { + t.Fatalf("generateWithCodeExecAndImg failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with func declaration and func response", func(t *testing.T) { + buf.Reset() + err := generateWithFuncCall(buf) + if err != nil { + t.Fatalf("generateWithFuncCall failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate with Google Search", func(t *testing.T) { + buf.Reset() + err := generateWithGoogleSearch(buf) + if err != nil { + t.Fatalf("generateWithGoogleSearch failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) +} diff --git a/genai/tools/tools_func_desc_with_txt.go b/genai/tools/tools_func_desc_with_txt.go new file mode 100644 index 0000000000..47c8aae758 --- /dev/null +++ b/genai/tools/tools_func_desc_with_txt.go @@ -0,0 +1,128 @@ +// 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 tools shows examples of various tools that Gemini model can use to generate text. +package tools + +// [START googlegenaisdk_tools_func_desc_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithFuncCall shows how to submit a prompt and a function declaration to the model, +// allowing it to suggest a call to the function to fetch external data. Returning this data +// enables the model to generate a text response that incorporates the data. +func generateWithFuncCall(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) + } + + weatherFunc := &genai.FunctionDeclaration{ + Description: "Returns the current weather in a location.", + Name: "getCurrentWeather", + Parameters: &genai.Schema{ + Type: "object", + Properties: map[string]*genai.Schema{ + "location": {Type: "string"}, + }, + Required: []string{"location"}, + }, + } + config := &genai.GenerateContentConfig{ + Tools: []*genai.Tool{ + {FunctionDeclarations: []*genai.FunctionDeclaration{weatherFunc}}, + }, + Temperature: genai.Ptr(0.0), + } + + modelName := "gemini-2.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What is the weather like in Boston?"}, + }}, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + var funcCall *genai.FunctionCall + for _, p := range resp.Candidates[0].Content.Parts { + if p.FunctionCall != nil { + funcCall = p.FunctionCall + fmt.Fprint(w, "The model suggests to call the function ") + fmt.Fprintf(w, "%q with args: %v\n", funcCall.Name, funcCall.Args) + // Example response: + // The model suggests to call the function "getCurrentWeather" with args: map[location:Boston] + } + } + if funcCall == nil { + return fmt.Errorf("model did not suggest a function call") + } + + // Use synthetic data to simulate a response from the external API. + // In a real application, this would come from an actual weather API. + funcResp := &genai.FunctionResponse{ + Name: "getCurrentWeather", + Response: map[string]any{ + "location": "Boston", + "temperature": "38", + "temperature_unit": "F", + "description": "Cold and cloudy", + "humidity": "65", + "wind": `{"speed": "10", "direction": "NW"}`, + }, + } + + // Return conversation turns and API response to complete the model's response. + contents = []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "What is the weather like in Boston?"}, + }}, + {Parts: []*genai.Part{ + {FunctionCall: funcCall}, + }}, + {Parts: []*genai.Part{ + {FunctionResponse: funcResp}, + }}, + } + + resp, err = client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // The weather in Boston is cold and cloudy with a temperature of 38 degrees Fahrenheit. The humidity is ... + + return nil +} + +// [END googlegenaisdk_tools_func_desc_with_txt] diff --git a/genai/tools/tools_google_search_with_txt.go b/genai/tools/tools_google_search_with_txt.go new file mode 100644 index 0000000000..9214d345ce --- /dev/null +++ b/genai/tools/tools_google_search_with_txt.go @@ -0,0 +1,67 @@ +// 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 tools shows examples of various tools that Gemini model can use to generate text. +package tools + +// [START googlegenaisdk_tools_google_search_with_txt] +import ( + "context" + "fmt" + "io" + + genai "google.golang.org/genai" +) + +// generateWithGoogleSearch shows how to generate text using Google Search. +func generateWithGoogleSearch(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.0-flash-001" + contents := []*genai.Content{ + {Parts: []*genai.Part{ + {Text: "When is the next total solar eclipse in the United States?"}, + }}, + } + config := &genai.GenerateContentConfig{ + Tools: []*genai.Tool{ + {GoogleSearch: &genai.GoogleSearch{}}, + }, + } + + resp, err := client.Models.GenerateContent(ctx, modelName, contents, config) + if err != nil { + return fmt.Errorf("failed to generate content: %w", err) + } + + respText, err := resp.Text() + if err != nil { + return fmt.Errorf("failed to convert model response to text: %w", err) + } + fmt.Fprintln(w, respText) + + // Example response: + // The next total solar eclipse in the United States will occur on March 30, 2033, but it will only ... + + return nil +} + +// [END googlegenaisdk_tools_google_search_with_txt] diff --git a/go.work b/go.work index 6cdbf80018..93521440ad 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,6 @@ go 1.23.0 -toolchain go1.23.4 +toolchain go1.23.5 use ( .