test: unify gemini 2 and gemini 3 thinking tests

Andrey Nering created

Change summary

providertests/google_test.go                                                           | 21 
providertests/testdata/TestGoogleThinking/gemini-2.5-flash/thinking-streaming.yaml     |  2 
providertests/testdata/TestGoogleThinking/gemini-2.5-flash/thinking.yaml               | 12 
providertests/testdata/TestGoogleThinking/gemini-2.5-pro/thinking-streaming.yaml       |  2 
providertests/testdata/TestGoogleThinking/gemini-2.5-pro/thinking.yaml                 | 14 
providertests/testdata/TestGoogleThinking/gemini-3-pro-preview/thinking-streaming.yaml |  6 
providertests/testdata/TestGoogleThinking/gemini-3-pro-preview/thinking.yaml           | 20 
7 files changed, 35 insertions(+), 42 deletions(-)

Detailed changes

providertests/google_test.go 🔗

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"net/http"
 	"os"
+	"strings"
 	"testing"
 
 	"charm.land/fantasy"
@@ -37,7 +38,7 @@ func TestGoogleCommon(t *testing.T) {
 }
 
 func TestGoogleThinking(t *testing.T) {
-	opts := fantasy.ProviderOptions{
+	gemini2Opts := fantasy.ProviderOptions{
 		google.Name: &google.ProviderOptions{
 			ThinkingConfig: &google.ThinkingConfig{
 				ThinkingBudget:  fantasy.Opt(int64(100)),
@@ -45,19 +46,7 @@ func TestGoogleThinking(t *testing.T) {
 			},
 		},
 	}
-
-	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 TestGoogleThinkingLevel(t *testing.T) {
-	opts := fantasy.ProviderOptions{
+	gemini3Opts := fantasy.ProviderOptions{
 		google.Name: &google.ProviderOptions{
 			ThinkingConfig: &google.ThinkingConfig{
 				ThinkingLevel:   fantasy.Opt(google.ThinkingLevelHigh),
@@ -71,6 +60,10 @@ func TestGoogleThinkingLevel(t *testing.T) {
 		if !m.reasoning {
 			continue
 		}
+		opts := gemini3Opts
+		if strings.HasPrefix(m.model, "gemini-2") {
+			opts = gemini2Opts
+		}
 		pairs = append(pairs, builderPair{m.name, geminiBuilder(m.model), opts, nil})
 	}
 	testThinking(t, pairs, testGoogleThinking)

providertests/testdata/TestGoogleThinking/gemini-2.5-flash/thinking-streaming.yaml 🔗

@@ -17,7 +17,7 @@ interactions:
       Content-Type:
       - application/json
       User-Agent:
-      - google-genai-sdk/1.34.0 gl-go/go1.25.4
+      - google-genai-sdk/1.47.0 gl-go/go1.26.0
     url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse
     method: POST
   response:
@@ -25,22 +25,22 @@ interactions:
     proto_major: 2
     proto_minor: 0
     content_length: -1

providertests/testdata/TestGoogleThinking/gemini-2.5-flash/thinking.yaml 🔗

@@ -14,7 +14,7 @@ interactions:
       Content-Type:
       - application/json
       User-Agent:
-      - google-genai-sdk/1.34.0 gl-go/go1.25.4
+      - google-genai-sdk/1.47.0 gl-go/go1.26.0
     url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
     method: POST
   response:
@@ -30,7 +30,7 @@ interactions:
             "content": {
               "parts": [
                 {
-                  "text": "**Weather Inquiry: Florence, Italy**\n\nOkay, so the user wants to know the weather. Seems straightforward enough. They've specified \"Florence, Italy,\" which is great, because I need a location to work with. Fortunately, I have a \"weather\" tool at my disposal, and it appears it's designed to take a `location` as input. Therefore, my next step is clear: I'll use the \"weather\" tool, feeding it \"Florence, Italy\" as the `location` parameter. That should give me the weather information the user is after.\n",
+                  "text": "Okay, here's my thought process regarding the user's request:\n\n**Analyzing the Query: Florence Weather**\n\nAlright, I see the user wants the weather for Florence, Italy. That's straightforward. The key here is the tool I have access to – the `weather` tool. I know this tool is designed for precisely this kind of query. Crucially, the tool documentation says it requires a `location` argument. So, my task is to parse the user's request and feed that location information to the tool. It's a classic input-process-output situation. The \"input\" is Florence, Italy, or at least, that's what I'll be using for the query, and the \"process\" is to send it to the weather tool with the location parameter. I'm ready to query!\n",
                   "thought": true
                 },
                 {
@@ -40,7 +40,7 @@ interactions:
                       "location": "Florence, Italy"
                     }
                   },
-                  "thoughtSignature": "CvYBAdHtim9qsdgUovUUbgAa9oML5LNXaG6UCnPcGzw1zOO0g3lKC3BMnvd5uNr1Dbu4iv/D1RFKhdpNvKq36C4OgfmARWRVkb6SCFEB2HliZgcq0eH24m1h1Wc3TgGsGNTm4Ppy61sth++CxfYNEZLnw+7xD/VhRMJn5a6/faKS4+MGzsH63NHx0DYdfpFpMytkoCa551Aunb8VOPy9KG2auvUT5KfWqf32mrjbeRhe3ngFDM7gBzYq/1AiVyQVluHSlvmHQ738klIpV7rZelCWzr+QgcPrc+T2MjHoXFtQQ5W24CHqHdW+ViYmcgpQT7QIM/1qCohh"
+                  "thoughtSignature": "CoACAb4+9vsBHFxHrQuonIUfSR6Nm17lrRyUzuM6N/Hcp+/BSAONP0z+zhyo4cp6LNYfVevZZJjYIbDZrdqY/Quk9hwF1CaGnxmSMl5S6MbEYyIyNckE7tHMcNMJu3ODw37XOzCltw40lrTQvZmvkSrmMy9+X45L0RXDOJf9mYy9V3BCwPBoFK5uJr+BsaXb/DAbbFoPJXKuN++xTywS1smdJRB8YcVq/oGdbgB8RY0+/2EfNWrHipiSq06vjvwuXyCJFdziEtsxWNqv3GvqCv+M7BfE3P3EXCRbiNfDmfLjilifRs1Xw12rK7wC9TMUS7b52d/k/Js/StKn1ww8CtRffA=="
                 }
               ],
               "role": "model"
@@ -63,28 +63,28 @@ interactions:
           "thoughtsTokenCount": 51
         },
         "modelVersion": "gemini-2.5-flash",
-        "responseId": "o6Idaab7Kdj9xN8PsaeMoAU"
+        "responseId": "eP-dacvXLKScz7IP_bysiAI"
       }
     headers:
       Content-Type:
       - application/json; charset=UTF-8
     status: 200 OK
     code: 200
-    duration: 1.776984417s
+    duration: 2.312526792s
 - id: 1
   request:
     proto: HTTP/1.1
     proto_major: 1
     proto_minor: 1
-    content_length: 1118
+    content_length: 1134
     host: generativelanguage.googleapis.com
     body: |

providertests/testdata/TestGoogleThinking/gemini-2.5-pro/thinking-streaming.yaml 🔗

@@ -17,7 +17,7 @@ interactions:
       Content-Type:
       - application/json
       User-Agent:
-      - google-genai-sdk/1.34.0 gl-go/go1.25.4
+      - google-genai-sdk/1.47.0 gl-go/go1.26.0
     url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse
     method: POST
   response:
@@ -25,22 +25,22 @@ interactions:
     proto_major: 2
     proto_minor: 0
     content_length: -1

providertests/testdata/TestGoogleThinking/gemini-2.5-pro/thinking.yaml 🔗

@@ -14,7 +14,7 @@ interactions:
       Content-Type:
       - application/json
       User-Agent:
-      - google-genai-sdk/1.34.0 gl-go/go1.25.4
+      - google-genai-sdk/1.47.0 gl-go/go1.26.0
     url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent
     method: POST
   response:
@@ -30,7 +30,7 @@ interactions:
             "content": {
               "parts": [
                 {
-                  "text": "**Getting the Weather in Florence**\n\nOkay, so I see the user wants to know the weather in Florence, Italy. That's straightforward; that query directly corresponds to my `weather.get_weather` function.  It's pretty clear-cut, the function takes a `location` parameter, and in this case, that `location` is simply \"Florence, Italy\". Nothing complicated here, I can just set that parameter and move on.\n",
+                  "text": "**Weather in Florence, Italy**\n\nOkay, so the user wants the weather for \"Florence, Italy.\" That's straightforward; my processing path leads directly to `weather.get_weather`. This is a clear case for that particular function. It takes a `location` argument, and that's easy enough. I'll just set the `location` parameter to \"Florence, Italy.\" Done! Ready to execute.\n",
                   "thought": true
                 },
                 {
@@ -40,7 +40,7 @@ interactions:
                       "location": "Florence, Italy"
                     }
                   },
-                  "thoughtSignature": "CpwCAdHtim+0FmI+WFq9+osYttd0V3wq5L6xpmfD+s3Xy+iz1L6F5MmUgDo3Vop462ydmq46DgqFSLYj33RAcxmXMnfJMnqdDbLkR00eT6FtPNKk1HxBIDg+d8nxH5LBRnQ4+/Py4nOa4oMiTfNaOuMpZZnIyJBRLFSOw0ZQftvmXoKCB/hZEW6dYve3wxGH+o4BZ/sdxO/3IdPhk2eRlcieDEVN/F4Fg1xAmdc/60inB/SsuSIMRRKHW/48nlCOwlJbbTRI9FIhtqLkDwjXMMWxwovwhstRb4+frvLsOIsBNQk+q5MByNI+waAOxhSVVvHChLh9vQTM4xZ0g14GGZ6SbLz+ujRWdPYjN1ON25uMX00ZTwM2S5K8IZUeJsM="
+                  "thoughtSignature": "CpwCAb4+9vsnDPVKCBbr2tnMUnEAJFGNMneg95rjCSOs6HV/dxMzyPDGxbqSkXe22liMoinB/fkfQFE9JI121uEeAjCBPYluDcHAuoOR6z3WdK+9x4gLfjsjieoOSGE+x9KkM0iml+XX+RP42tuBsOEn5AQsvr3iUQ/rdo1hanDOSdT0RfNyWmwYdGNAHPFsehZN3FAEuMCKEPS5bVsh6rGMpccdFYLHy9hnLIg0WZFZwSwhmGNSXuBUc6YK71TtMRxe19WuRbrXA4Na2ig9pBWIcOtYwaeTRcYmaZoh5agdiG9G4tWZCQ1Kbxsg1FZ8jXXrlPefL2jwtgDcidWKt3BQ3XvGm1db0I+H08+LOZa6ITpOd0qtIc4Pc2T/EmE="
                 }
               ],
               "role": "model"
@@ -53,24 +53,24 @@ interactions:
         "usageMetadata": {
           "promptTokenCount": 54,
           "candidatesTokenCount": 15,
-          "totalTokenCount": 135,
+          "totalTokenCount": 133,
           "promptTokensDetails": [
             {
               "modality": "TEXT",
               "tokenCount": 54
             }
           ],
-          "thoughtsTokenCount": 66
+          "thoughtsTokenCount": 64
         },
         "modelVersion": "gemini-2.5-pro",
-        "responseId": "qqIdaZ73A_ClvdIPvJaP0Ak"
+        "responseId": "gP-dacn4HJCDz7IP58yLgAc"
       }
     headers:
       Content-Type:
       - application/json; charset=UTF-8
     status: 200 OK
     code: 200
-    duration: 3.080074125s
+    duration: 2.659325375s
 - id: 1
   request:
     proto: HTTP/1.1
@@ -79,12 +79,12 @@ interactions:
     content_length: 1170
     host: generativelanguage.googleapis.com
     body: |

providertests/testdata/TestGoogleThinking/gemini-3-pro-preview/thinking-streaming.yaml 🔗

@@ -6,10 +6,10 @@ interactions:
     proto: HTTP/1.1
     proto_major: 1
     proto_minor: 1
-    content_length: 550
+    content_length: 552
     host: generativelanguage.googleapis.com
     body: |
-      {"contents":[{"parts":[{"text":"What's the weather in Florence, Italy?"}],"role":"user"}],"generationConfig":{"thinkingConfig":{"includeThoughts":true,"thinkingBudget":128}},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"},"toolConfig":{"functionCallingConfig":{"mode":"AUTO"}},"tools":[{"functionDeclarations":[{"description":"Get weather information for a location","name":"weather","parameters":{"properties":{"location":{"description":"the city","type":"STRING"}},"required":["location"],"type":"OBJECT"}}]}]}
+      {"contents":[{"parts":[{"text":"What's the weather in Florence, Italy?"}],"role":"user"}],"generationConfig":{"thinkingConfig":{"includeThoughts":true,"thinkingLevel":"HIGH"}},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"},"toolConfig":{"functionCallingConfig":{"mode":"AUTO"}},"tools":[{"functionDeclarations":[{"description":"Get weather information for a location","name":"weather","parameters":{"properties":{"location":{"description":"the city","type":"STRING"}},"required":["location"],"type":"OBJECT"}}]}]}
     form:
       alt:
       - sse
@@ -17,7 +17,7 @@ interactions:
       Content-Type:
       - application/json
       User-Agent:
-      - google-genai-sdk/1.34.0 gl-go/go1.25.4
+      - google-genai-sdk/1.47.0 gl-go/go1.26.0
     url: https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-preview:streamGenerateContent?alt=sse
     method: POST
   response:
@@ -25,22 +25,22 @@ interactions:
     proto_major: 2
     proto_minor: 0
     content_length: -1

providertests/testdata/TestGoogleThinking/gemini-3-pro-preview/thinking.yaml 🔗

@@ -6,15 +6,15 @@ interactions:
     proto: HTTP/1.1
     proto_major: 1
     proto_minor: 1
-    content_length: 550
+    content_length: 552
     host: generativelanguage.googleapis.com
     body: |
-      {"contents":[{"parts":[{"text":"What's the weather in Florence, Italy?"}],"role":"user"}],"generationConfig":{"thinkingConfig":{"includeThoughts":true,"thinkingBudget":128}},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"},"toolConfig":{"functionCallingConfig":{"mode":"AUTO"}},"tools":[{"functionDeclarations":[{"description":"Get weather information for a location","name":"weather","parameters":{"properties":{"location":{"description":"the city","type":"STRING"}},"required":["location"],"type":"OBJECT"}}]}]}
+      {"contents":[{"parts":[{"text":"What's the weather in Florence, Italy?"}],"role":"user"}],"generationConfig":{"thinkingConfig":{"includeThoughts":true,"thinkingLevel":"HIGH"}},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"},"toolConfig":{"functionCallingConfig":{"mode":"AUTO"}},"tools":[{"functionDeclarations":[{"description":"Get weather information for a location","name":"weather","parameters":{"properties":{"location":{"description":"the city","type":"STRING"}},"required":["location"],"type":"OBJECT"}}]}]}
     headers:
       Content-Type:
       - application/json
       User-Agent:
-      - google-genai-sdk/1.34.0 gl-go/go1.25.4
+      - google-genai-sdk/1.47.0 gl-go/go1.26.0
     url: https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-preview:generateContent
     method: POST
   response:
@@ -30,7 +30,7 @@ interactions:
             "content": {
               "parts": [
                 {
-                  "text": "**Breaking Down the Weather Query**\n\nOkay, here's what I'm processing. A user is asking about the weather in Florence, Italy. That's straightforward enough - they want current weather data. Looking at my toolset, I see the `weather` tool is perfect for this. It's explicitly designed for retrieving weather information based on a location. Easy. This is a classic example of intent recognition and tool selection. My next step would be to formulate the `weather` function call with the correct parameters, specifically the location, \"Florence, Italy\". I'm ready to get this user the information they need.\n\n\n",
+                  "text": "**My Approach to Gathering Weather Information**\n\nOkay, so the user wants the weather for Florence, Italy. My primary task here is to find the most efficient way to get that information. I immediately recognize the need for a weather data retrieval tool; that's the obvious starting point. I quickly scan my available tools and, from what I see, the `weather` tool seems like the most relevant option. That's a good sign.\n\nNow, I need to ensure I'm using this tool correctly. It takes a `location` parameter, which is exactly what I need to specify the city. Excellent, that aligns perfectly with the user's request for Florence. Therefore, the logical action is to call the `weather` tool and pass \"Florence, Italy\" as the value for the `location` parameter. The structure will be `weather(location=\"Florence, Italy\")`. That should provide the user with the weather details they're after.\n\n\n",
                   "thought": true
                 },
                 {
@@ -40,7 +40,7 @@ interactions:
                       "location": "Florence, Italy"
                     }
                   },
-                  "thoughtSignature": "EtMDCtADAdHtim/Qznjl3jYdbqtczyFkf1sarL7CYPmwkwMTU8qifm7AQe1i21NWqWvbSGT+KG/NkCBhcC0Zc8MusHqgFm2Iw+2TP0Ckp8nL5k+1G7jffxPRKsXcyq/6jvRtGvcxb199vZTNapt2/vYas7tEOZahNr/Xiuic7yVEcEEQD8Vdsp9qgRy8qu4RO1nc8Q5cnbd8fOEKfjusde2ehpTA47PUNhhIWlLBEGeMOowAoaEf9/W7cMxlg21omQYaYiIVgvI1qi2U/4nSOKDBlTA2N0LDuygPTtRsz2ZMwJGC8CIrrCW0apY1X2QcgO+s/9tNurJp/D9tiIFnwRXT8q7V90nAng9dSqHrChr2jvr4n3j5wgbvHnqLJouCKR8ME+leQZUHefdJyHeXNSjv9Rzs7cqiaUgWR7Rn11BQld3Q/myVqq7/r0LI/IchztYQVKDvE0XXbc0o8x4cfQTmtdlSwcAQxE5OG6sLNoRPAJmPfkwlppeHwNGG7ntkCKX/nG/o40w2OyFwk6WnkyNhnqTiA5knDyP51fnOiwKhFYxtDa7f2WdUPsILiJjqT7hDjtYTCnYvuWgTvqkaRtfLWES9QIVVZP63zK6PDFQLtLp8kd0="
+                  "thoughtSignature": "EoMDCoADAb4+9vvcql04LjFTQa1nTMMudhrlpmFSqgQWtecZs9Gzm8GC6izmq42etV5ICgg5qr/h5KO05LHjo7OT/Y/O6zTq5vweOOvvAZHK3IDBgU7FBiCW5bW+nI6TjhgDHSNPn/Uk14iuv5RGXrH1QgxQ2ApBiLQlEmkjzmzmBUgI0gRmjRX+sDMF0IJu66tmZfkaqvR9Tp2xxEZjJitWL7P6jZvql6JE54UmL8dI02Dk/xnNS76rZiipzg8013aXxfIEgTEHMnsRvzK7QCGaukQ+/DU8GnomhZRYZPEW32OxQEjUyqm10c8o62WPtThyfIUmUBHwc8oojkH/2IT5fRy2nJwvUar3O82/of2miqe/9z7TNTKNK83wl9Gy5R3Gbc/XjI7A+LrwlYTynsVRqXFg3YURlzRnfFcGt4KEUcd2wLKqpeCNSngzS0VgiEqK5j9RTPedm3S2/oTgtl8jt2gff3NEDkJe8xUlXLS+l28rJ22Loc9BooDebKZYBBHvL9qu"
                 }
               ],
               "role": "model"
@@ -53,38 +53,38 @@ interactions:
         "usageMetadata": {
           "promptTokenCount": 65,
           "candidatesTokenCount": 16,
-          "totalTokenCount": 189,
+          "totalTokenCount": 161,
           "promptTokensDetails": [
             {
               "modality": "TEXT",
               "tokenCount": 65
             }
           ],
-          "thoughtsTokenCount": 108
+          "thoughtsTokenCount": 80
         },
         "modelVersion": "gemini-3-pro-preview",
-        "responseId": "mqIdabO3C5S3vdIPuPm34Ak"
+        "responseId": "cf-dacuQCbPoz7IPiZ25mAE"
       }
     headers:
       Content-Type:
       - application/json; charset=UTF-8
     status: 200 OK
     code: 200
-    duration: 3.163927334s
+    duration: 4.104216583s
 - id: 1
   request:
     proto: HTTP/1.1
     proto_major: 1
     proto_minor: 1
-    content_length: 1414
+    content_length: 1308
     host: generativelanguage.googleapis.com
     body: |