diff --git a/go/plugins/googlegenai/gemini.go b/go/plugins/googlegenai/gemini.go index d866763ec..c979ebae5 100644 --- a/go/plugins/googlegenai/gemini.go +++ b/go/plugins/googlegenai/gemini.go @@ -175,6 +175,14 @@ type SafetySetting struct { Threshold HarmBlockThreshold `json:"threshold,omitempty"` } +// Thinking configuration to control reasoning +type ThinkingConfig struct { + // Indicates whether the response should include thoughts (if available and supported) + IncludeThoughts bool `json:"includeThoughts,omitempty"` + // Thinking budget in tokens. If set to zero, thinking gets disabled + ThinkingBudget int32 `json:"thinkingBudget,omitempty"` +} + type Modality string const ( @@ -204,6 +212,8 @@ type GeminiConfig struct { CodeExecution bool `json:"codeExecution,omitempty"` // Response modalities for returned model messages ResponseModalities []Modality `json:"responseModalities,omitempty"` + // Thinking configuration controls the model's internal reasoning process + ThinkingConfig *ThinkingConfig `json:"thinkingConfig,omitempty"` } // configFromRequest converts any supported config type to [GeminiConfig]. @@ -528,6 +538,13 @@ func toGeminiRequest(input *ai.ModelRequest, cache *genai.CachedContent) (*genai }) } + if c.ThinkingConfig != nil { + gcc.ThinkingConfig = &genai.ThinkingConfig{ + IncludeThoughts: c.ThinkingConfig.IncludeThoughts, + ThinkingBudget: &c.ThinkingConfig.ThinkingBudget, + } + } + var systemParts []*genai.Part for _, m := range input.Messages { if m.Role == ai.RoleSystem { @@ -767,6 +784,10 @@ func translateCandidate(cand *genai.Candidate) *ai.ModelResponse { if part.Text != "" { partFound++ + if part.Thought { + // TODO: Include a `reasoning` part. Not available in the SDK yet. + continue + } p = ai.NewTextPart(part.Text) } if part.InlineData != nil { diff --git a/go/plugins/googlegenai/gemini_test.go b/go/plugins/googlegenai/gemini_test.go index 2e81d4aae..8d8244fc8 100644 --- a/go/plugins/googlegenai/gemini_test.go +++ b/go/plugins/googlegenai/gemini_test.go @@ -44,6 +44,10 @@ func TestConvertRequest(t *testing.T) { TopK: 1.0, TopP: 1.0, Version: text, + ThinkingConfig: &ThinkingConfig{ + IncludeThoughts: false, + ThinkingBudget: 0, + }, }, Tools: []*ai.ToolDefinition{tool}, ToolChoice: ai.ToolChoiceAuto, @@ -121,6 +125,9 @@ func TestConvertRequest(t *testing.T) { if gcc.ResponseSchema == nil { t.Errorf("ResponseSchema should not be empty") } + if gcc.ThinkingConfig == nil { + t.Errorf("ThinkingConfig should not be empty") + } }) t.Run("convert tools with valid tool", func(t *testing.T) { tools := []*ai.ToolDefinition{tool} diff --git a/go/plugins/googlegenai/googleai_live_test.go b/go/plugins/googlegenai/googleai_live_test.go index 61a844b96..5c51cd164 100644 --- a/go/plugins/googlegenai/googleai_live_test.go +++ b/go/plugins/googlegenai/googleai_live_test.go @@ -398,6 +398,48 @@ func TestGoogleAILive(t *testing.T) { t.Errorf("Empty usage stats %#v", *resp.Usage) } }) + t.Run("thinking", func(t *testing.T) { + m := googlegenai.GoogleAIModel(g, "gemini-2.5-flash-preview-04-17") + resp, err := genkit.Generate(ctx, g, + ai.WithConfig(googlegenai.GeminiConfig{ + Temperature: 0.4, + ThinkingConfig: &googlegenai.ThinkingConfig{ + IncludeThoughts: true, + ThinkingBudget: 1024, + }, + }), + ai.WithModel(m), + ai.WithPrompt("Analogize photosynthesis and growing up.")) + if err != nil { + t.Fatal(err) + } + if resp == nil { + t.Fatal("nil response obtanied") + } + // TODO: add usageMetadata validation when SDK provides int + // see https://github.com/googleapis/go-genai/issues/282 + }) + t.Run("thinking disabled", func(t *testing.T) { + m := googlegenai.GoogleAIModel(g, "gemini-2.5-flash-preview-04-17") + resp, err := genkit.Generate(ctx, g, + ai.WithConfig(googlegenai.GeminiConfig{ + Temperature: 0.4, + ThinkingConfig: &googlegenai.ThinkingConfig{ + IncludeThoughts: false, + ThinkingBudget: 0, + }, + }), + ai.WithModel(m), + ai.WithPrompt("Analogize photosynthesis and growing up.")) + if err != nil { + t.Fatal(err) + } + if resp == nil { + t.Fatal("nil response obtanied") + } + // TODO: add usageMetadata validation when SDK provides int + // see https://github.com/googleapis/go-genai/issues/282 + }) } func TestCacheHelper(t *testing.T) {