diff --git a/genai/go.mod b/genai/go.mod index 9098281bb3..1505975d3d 100644 --- a/genai/go.mod +++ b/genai/go.mod @@ -4,7 +4,7 @@ go 1.24.0 require ( github.com/GoogleCloudPlatform/golang-samples v0.0.0-20250201051611-5fb145d1e974 - google.golang.org/genai v1.17.0 + google.golang.org/genai v1.25.0 ) require ( diff --git a/genai/go.sum b/genai/go.sum index 263bb22be2..497bc3bff2 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 v1.17.0 h1:lXYSnWShPYjxTouxRj0zF8RsNmSF+SKo7SQ7dM35NlI= -google.golang.org/genai v1.17.0/go.mod h1:QPj5NGJw+3wEOHg+PrsWwJKvG6UC84ex5FR7qAYsN/M= +google.golang.org/genai v1.25.0 h1:Cpyh2nmEoOS1eM3mT9XKuA/qWTEDoktfP2gsN3EduPE= +google.golang.org/genai v1.25.0/go.mod h1:OClfdf+r5aaD+sCd4aUSkPzJItmg2wD/WON9lQnRPaY= 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/image_generation/image_generation_examples_test.go b/genai/image_generation/image_generation_examples_test.go new file mode 100644 index 0000000000..28ac859ad8 --- /dev/null +++ b/genai/image_generation/image_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 image_generation + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestImageGeneration(t *testing.T) { + tc := testutil.SystemTest(t) + + t.Setenv("GOOGLE_GENAI_USE_VERTEXAI", "1") + t.Setenv("GOOGLE_CLOUD_LOCATION", "global") + t.Setenv("GOOGLE_CLOUD_PROJECT", tc.ProjectID) + + buf := new(bytes.Buffer) + + t.Run("generate mmflash locale aware image content with text", func(t *testing.T) { + buf.Reset() + err := generateMMFlashLocaleAwareWithText(buf) + if err != nil { + t.Fatalf("generateMMFlashLocaleAwareWithText failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("generate mmflash multiple images with text", func(t *testing.T) { + buf.Reset() + err := generateMMFlashMultipleImgsWithText(buf) + if err != nil { + t.Fatalf("generateMMFlashMultipleImgsWithText failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) + + t.Run("virtual try-on generation", func(t *testing.T) { + buf.Reset() + err := generateImgVirtualTryOnWithTextImg(buf) + if err != nil { + t.Fatalf("generateImgVirtualTryOnWithTextImg failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) +} diff --git a/genai/image_generation/imggen_mmflash_locale_aware_with_txt.go b/genai/image_generation/imggen_mmflash_locale_aware_with_txt.go new file mode 100644 index 0000000000..22ade488a6 --- /dev/null +++ b/genai/image_generation/imggen_mmflash_locale_aware_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 image_generation shows how to use the GenAI SDK to generate images and text. +package image_generation + +// [START googlegenaisdk_imggen_mmflash_locale_aware_with_txt] +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + + "google.golang.org/genai" +) + +// generateMMFlashLocaleAwareWithText demonstrates how to generate an image with locale awareness. +func generateMMFlashLocaleAwareWithText(w io.Writer) error { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1"}, + }) + if err != nil { + return fmt.Errorf("failed to create genai client: %w", err) + } + + modelName := "gemini-2.5-flash-image" + prompt := "Generate a photo of a breakfast meal." + contents := []*genai.Content{ + { + Parts: []*genai.Part{ + {Text: prompt}, + }, + Role: "user", + }, + } + + resp, err := client.Models.GenerateContent(ctx, + modelName, + contents, + &genai.GenerateContentConfig{ + ResponseModalities: []string{ + string(genai.ModalityText), + string(genai.ModalityImage), + }, + }, + ) + if err != nil { + return fmt.Errorf("generate content failed: %w", err) + } + + if len(resp.Candidates) == 0 || resp.Candidates[0].Content == nil { + return fmt.Errorf("no content was generated") + } + + outputPath := filepath.Join("testdata", "example-breakfast-meal.png") + + for _, part := range resp.Candidates[0].Content.Parts { + switch { + case part.Text != "": + fmt.Fprintln(w, part.Text) + case part.InlineData != nil: + if err := os.WriteFile(outputPath, part.InlineData.Data, 0o644); err != nil { + return fmt.Errorf("failed to save generated image: %w", err) + } + } + } + fmt.Fprintln(w, outputPath) + + // Example response: + // Here is a photo of a delicious breakfast meal for you! ... + + return nil +} + +// [END googlegenaisdk_imggen_mmflash_locale_aware_with_txt] diff --git a/genai/image_generation/imggen_mmflash_multiple_imgs_with_txt.go b/genai/image_generation/imggen_mmflash_multiple_imgs_with_txt.go new file mode 100644 index 0000000000..14a72d8d6d --- /dev/null +++ b/genai/image_generation/imggen_mmflash_multiple_imgs_with_txt.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 image_generation shows how to use the GenAI SDK to generate images and text. +package image_generation + +// [START googlegenaisdk_imggen_mmflash_multiple_imgs_with_txt] +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + + "google.golang.org/genai" +) + +// generateMMFlashMultipleImgsWithText demonstrates how to generate multiple images with text. +func generateMMFlashMultipleImgsWithText(w io.Writer) error { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1"}, + }) + if err != nil { + return fmt.Errorf("failed to create genai client: %w", err) + } + + modelName := "gemini-2.5-flash-image" + prompt := "Generate 3 images a cat sitting on a chair." + contents := []*genai.Content{ + { + Parts: []*genai.Part{ + {Text: prompt}, + }, + Role: "user", + }, + } + + resp, err := client.Models.GenerateContent(ctx, + modelName, + contents, + &genai.GenerateContentConfig{ + ResponseModalities: []string{ + string(genai.ModalityText), + string(genai.ModalityImage), + }, + }, + ) + if err != nil { + return fmt.Errorf("generate content failed: %w", err) + } + + if len(resp.Candidates) == 0 || resp.Candidates[0].Content == nil { + return fmt.Errorf("no content was generated") + } + + outputDir := filepath.Join("testdata") + + imageCounter := 1 + for _, part := range resp.Candidates[0].Content.Parts { + switch { + case part.Text != "": + fmt.Fprintln(w, part.Text) + case part.InlineData != nil: + filename := filepath.Join(outputDir, fmt.Sprintf("example-cats-0%d.png", imageCounter)) + if err := os.WriteFile(filename, part.InlineData.Data, 0o644); err != nil { + return fmt.Errorf("failed to save generated image: %w", err) + } + fmt.Fprintln(w, filename) + imageCounter++ + } + } + + // Example response: + // Image 1: A fluffy calico cat with striking green eyes is perched elegantly on a vintage wooden + // chair with a woven seat. Sunlight streams through a nearby window, casting soft shadows and + // highlighting the cat's fur. + // # + // Image 2: A sleek black cat with intense yellow eyes is sitting upright on a modern, minimalist + // white chair. The background is a plain grey wall, putting the focus entirely on the feline's + // graceful posture. + // # + // Image 3: A ginger tabby cat with playful amber eyes is comfortably curled up asleep on a plush, + // oversized armchair upholstered in a soft, floral fabric. A corner of a cozy living room with a + // warm lamp in the background can be seen. + + return nil +} + +// [END googlegenaisdk_imggen_mmflash_multiple_imgs_with_txt] diff --git a/genai/image_generation/imggen_virtual_try_on_with_txt_img.go b/genai/image_generation/imggen_virtual_try_on_with_txt_img.go new file mode 100644 index 0000000000..afcf8094a1 --- /dev/null +++ b/genai/image_generation/imggen_virtual_try_on_with_txt_img.go @@ -0,0 +1,100 @@ +// 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 image_generation shows how to use the GenAI SDK to generate images and text. +package image_generation + +// [START googlegenaisdk_imggen_virtual_try_on_with_txt_img] +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + + "google.golang.org/genai" +) + +// generateImgVirtualTryOnWithTextImg demonstrates how to apply a product image to a person image. +func generateImgVirtualTryOnWithTextImg(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 := "virtual-try-on-preview-08-04" + + // Load local person image + personBytes, err := os.ReadFile("testdata/man.png") + if err != nil { + return fmt.Errorf("failed to read person image: %w", err) + } + personImage := &genai.Image{ + ImageBytes: personBytes, + MIMEType: "image/png", + } + + // Load local product image + productBytes, err := os.ReadFile("testdata/sweater.jpg") + if err != nil { + return fmt.Errorf("failed to read product image: %w", err) + } + productImage := &genai.ProductImage{ + ProductImage: &genai.Image{ + ImageBytes: productBytes, + MIMEType: "image/jpeg", + }, + } + + resp, err := client.Models.RecontextImage(ctx, + modelName, + &genai.RecontextImageSource{ + PersonImage: personImage, + ProductImages: []*genai.ProductImage{productImage}, + }, nil, + ) + if err != nil { + return fmt.Errorf("recontext image failed: %w", err) + } + + // Ensure we have a generated image and save it + if len(resp.GeneratedImages) == 0 || resp.GeneratedImages[0].Image == nil { + return fmt.Errorf("no image was generated") + } + + // TODO(developer): Update below lines + path := "testdata" + outputFile := "man_in_sweater.png" + + // Save output + outputPath := filepath.Join(path, outputFile) + + if err := os.WriteFile(outputPath, resp.GeneratedImages[0].Image.ImageBytes, 0o644); err != nil { + return fmt.Errorf("failed to save generated image: %w", err) + } + + fmt.Fprintf(w, "Created output image using %d bytes\n", len(resp.GeneratedImages[0].Image.ImageBytes)) + fmt.Fprintln(w, outputPath) + + // Example response: + // Created output image using 1636301 bytes ... + + return nil +} + +// [END googlegenaisdk_imggen_virtual_try_on_with_txt_img] diff --git a/genai/image_generation/testdata/man.png b/genai/image_generation/testdata/man.png new file mode 100644 index 0000000000..7cf652e8e6 Binary files /dev/null and b/genai/image_generation/testdata/man.png differ diff --git a/genai/image_generation/testdata/sweater.jpg b/genai/image_generation/testdata/sweater.jpg new file mode 100644 index 0000000000..69cc18f921 Binary files /dev/null and b/genai/image_generation/testdata/sweater.jpg differ diff --git a/genai/thinking/thinking_budget_with_txt.go b/genai/thinking/thinking_budget_with_txt.go new file mode 100644 index 0000000000..7ab08dbf9d --- /dev/null +++ b/genai/thinking/thinking_budget_with_txt.go @@ -0,0 +1,86 @@ +// 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 thinking shows how to use the GenAI SDK to include thoughts with txt. +package thinking + +// [START googlegenaisdk_thinking_budget_with_txt] +import ( + "context" + "fmt" + "io" + + "google.golang.org/genai" +) + +// generateThinkingBudgetContentWithText demonstrates how to generate text including the model's thought process. +func generateThinkingBudgetContentWithText(w io.Writer) error { + ctx := context.Background() + + client, err := genai.NewClient(ctx, &genai.ClientConfig{ + HTTPOptions: genai.HTTPOptions{APIVersion: "v1"}, + }) + if err != nil { + return fmt.Errorf("failed to create genai client: %w", err) + } + + modelName := "gemini-2.5-flash" + thinkingBudget := int32(1024) //Use `0` to turn off thinking + contents := []*genai.Content{ + { + Parts: []*genai.Part{ + {Text: "solve x^2 + 4x + 4 = 0"}, + }, + Role: "user", + }, + } + + resp, err := client.Models.GenerateContent(ctx, + modelName, + contents, + &genai.GenerateContentConfig{ + ThinkingConfig: &genai.ThinkingConfig{ + ThinkingBudget: &thinkingBudget, + }, + }, + ) + if err != nil { + return fmt.Errorf("generate content failed: %w", err) + } + + if resp.UsageMetadata != nil { + fmt.Fprintf(w, "Thoughts token count: %d\n", resp.UsageMetadata.ThoughtsTokenCount) + //Example response: + // 908 + fmt.Fprintf(w, "Total token count: %d\n", resp.UsageMetadata.TotalTokenCount) + //Example response: + // 1364 + } + + fmt.Fprintln(w, resp.Text()) + + // Example response: + // To solve the equation $x^2 + 4x + 4 = 0$, you can use several methods: + // **Method 1: Factoring** + // 1. Look for two numbers that multiply to the constant term (4) and add up to the coefficient of the $x$ term (4). + // 2. The numbers are 2 and 2 ($2 \times 2 = 4$ and $2 + 2 = 4$). + // ... + // ... + // Both methods yield the same result. + // The solution to the equation $x^2 + 4x + 4 = 0$ is **$x = -2$**. + + return nil +} + +// [END googlegenaisdk_thinking_budget_with_txt] diff --git a/genai/thinking/thinking_examples_test.go b/genai/thinking/thinking_examples_test.go new file mode 100644 index 0000000000..3b259c25ba --- /dev/null +++ b/genai/thinking/thinking_examples_test.go @@ -0,0 +1,45 @@ +// 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 thinking + +import ( + "bytes" + "testing" + + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestThinkingGeneration(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 thinking budget content with text", func(t *testing.T) { + buf.Reset() + err := generateThinkingBudgetContentWithText(buf) + if err != nil { + t.Fatalf("generateThinkingBudgetContentWithText failed: %v", err) + } + + output := buf.String() + if output == "" { + t.Error("expected non-empty output, got empty") + } + }) +}