Detailed changes
@@ -242,6 +242,14 @@ func (g languageModel) prepareParams(call fantasy.Call) (*genai.GenerateContentC
})
providerOptions.ThinkingConfig.ThinkingBudget = fantasy.Opt(int64(128))
}
+
+ if providerOptions.ThinkingConfig.ThinkingLevel != nil &&
+ providerOptions.ThinkingConfig.ThinkingBudget != nil {
+ return nil, nil, nil, &fantasy.Error{
+ Title: "invalid argument",
+ Message: "thinking_level and thinking_budget are mutually exclusive",
+ }
+ }
}
isGemmaModel := strings.HasPrefix(strings.ToLower(g.modelID), "gemma-")
@@ -298,6 +306,9 @@ func (g languageModel) prepareParams(call fantasy.Call) (*genai.GenerateContentC
tmp := int32(*providerOptions.ThinkingConfig.ThinkingBudget) //nolint: gosec
config.ThinkingConfig.ThinkingBudget = &tmp
}
+ if providerOptions.ThinkingConfig.ThinkingLevel != nil {
+ config.ThinkingConfig.ThinkingLevel = genai.ThinkingLevel(*providerOptions.ThinkingConfig.ThinkingLevel)
+ }
}
for _, safetySetting := range providerOptions.SafetySettings {
config.SafetySettings = append(config.SafetySettings, &genai.SafetySetting{
@@ -31,10 +31,23 @@ func init() {
})
}
+// ThinkingLevel controls the amount of thinking a model does.
+// Use this for Gemini 3+ models instead of ThinkingBudget.
+// Mutually exclusive with ThinkingBudget.
+type ThinkingLevel = string
+
+const (
+ ThinkingLevelLow ThinkingLevel = "LOW"
+ ThinkingLevelMedium ThinkingLevel = "MEDIUM"
+ ThinkingLevelHigh ThinkingLevel = "HIGH"
+ ThinkingLevelMinimal ThinkingLevel = "MINIMAL"
+)
+
// ThinkingConfig represents thinking configuration for the Google provider.
type ThinkingConfig struct {
- ThinkingBudget *int64 `json:"thinking_budget"`
- IncludeThoughts *bool `json:"include_thoughts"`
+ ThinkingBudget *int64 `json:"thinking_budget,omitempty"`
+ IncludeThoughts *bool `json:"include_thoughts,omitempty"`
+ ThinkingLevel *string `json:"thinking_level,omitempty"`
}
// ReasoningMetadata represents reasoning metadata for the Google provider.
@@ -56,6 +56,26 @@ func TestGoogleThinking(t *testing.T) {
testThinking(t, pairs, testGoogleThinking)
}
+func TestGoogleThinkingLevel(t *testing.T) {
+ opts := fantasy.ProviderOptions{
+ google.Name: &google.ProviderOptions{
+ ThinkingConfig: &google.ThinkingConfig{
+ ThinkingLevel: fantasy.Opt(google.ThinkingLevelHigh),
+ IncludeThoughts: fantasy.Opt(true),
+ },
+ },
+ }
+
+ var pairs []builderPair
+ for _, m := range geminiTestModels {
+ if !m.reasoning {
+ continue
+ }
+ pairs = append(pairs, builderPair{m.name, geminiBuilder(m.model), opts, nil})
+ }
+ testThinking(t, pairs, testGoogleThinking)
+}
+
func TestGoogleObjectGeneration(t *testing.T) {
var pairs []builderPair
for _, m := range geminiTestModels {
@@ -181,6 +181,9 @@ func TestProviderRegistry_Serialization_GoogleOptions(t *testing.T) {
google.Name: &google.ProviderOptions{
CachedContent: "cached-123",
Threshold: "BLOCK_ONLY_HIGH",
+ ThinkingConfig: &google.ThinkingConfig{
+ ThinkingLevel: fantasy.Opt(google.ThinkingLevelHigh),
+ },
},
},
}
@@ -197,6 +200,9 @@ func TestProviderRegistry_Serialization_GoogleOptions(t *testing.T) {
require.True(t, ok)
require.Equal(t, "cached-123", opt.CachedContent)
require.Equal(t, "BLOCK_ONLY_HIGH", opt.Threshold)
+ require.NotNil(t, opt.ThinkingConfig)
+ require.NotNil(t, opt.ThinkingConfig.ThinkingLevel)
+ require.Equal(t, google.ThinkingLevelHigh, *opt.ThinkingConfig.ThinkingLevel)
}
func TestProviderRegistry_Serialization_OpenRouterOptions(t *testing.T) {