fix: fix thinking on/off toggle for certain openai-compat providers

Andrey Nering created

Change summary

go.mod                        |  4 ++--
go.sum                        |  8 ++++----
internal/agent/coordinator.go | 30 +++++++++++++++++++++++++++++-
3 files changed, 35 insertions(+), 7 deletions(-)

Detailed changes

go.mod 🔗

@@ -5,9 +5,9 @@ go 1.26.2
 require (
 	charm.land/bubbles/v2 v2.1.0
 	charm.land/bubbletea/v2 v2.0.6
-	charm.land/catwalk v0.38.0
+	charm.land/catwalk v0.39.1
 	charm.land/fang/v2 v2.0.1
-	charm.land/fantasy v0.22.0
+	charm.land/fantasy v0.23.0
 	charm.land/glamour/v2 v2.0.0
 	charm.land/lipgloss/v2 v2.0.3
 	charm.land/log/v2 v2.0.0

go.sum 🔗

@@ -2,12 +2,12 @@ charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
 charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
 charm.land/bubbletea/v2 v2.0.6 h1:UHN/91OyuhaOFGSrBXQ/hMZD8IO1Uc4BvHlgHXL2WJo=
 charm.land/bubbletea/v2 v2.0.6/go.mod h1:MH/D8ZLlN3op37vQvijKuU29g3rqTp+aQapURFonF9g=
-charm.land/catwalk v0.38.0 h1:GAceUURVv7o3DQFAiS5z+UbLP0Xwkr70QvDIfqC7pyw=
-charm.land/catwalk v0.38.0/go.mod h1:IvMXm7qMKvYuWWlN5A4dA0bTgYg4clG1GAeU9/NlY0w=
+charm.land/catwalk v0.39.1 h1:jhMgnJQzYULdxoU7jf+Ct+ickBfGrxVEaylonIKCLWE=
+charm.land/catwalk v0.39.1/go.mod h1:IvMXm7qMKvYuWWlN5A4dA0bTgYg4clG1GAeU9/NlY0w=
 charm.land/fang/v2 v2.0.1 h1:zQCM8JQJ1JnQX/66B5jlCYBUxL2as5JXQZ2KJ6EL0mY=
 charm.land/fang/v2 v2.0.1/go.mod h1:S1GmkpcvK+OB5w9caywUnJcsMew45Ot8FXqoz8ALrII=
-charm.land/fantasy v0.22.0 h1:5HrjUr+52o0DW90//Vx1wYg+qcxaN21c2PeYeGVTb4U=
-charm.land/fantasy v0.22.0/go.mod h1:4yzSsd9XmFEVjRnF1P0LTEbLTmQX6OLnPkrHaf7iruo=
+charm.land/fantasy v0.23.0 h1:pocjwC5CxfEg1Bpwb0raML2d5ijo3op33Mmd6hYJyo4=
+charm.land/fantasy v0.23.0/go.mod h1:4yzSsd9XmFEVjRnF1P0LTEbLTmQX6OLnPkrHaf7iruo=
 charm.land/glamour/v2 v2.0.0 h1:IDBoqLEy7Hdpb9VOXN+khLP/XSxtJy1VsHuW/yF87+U=
 charm.land/glamour/v2 v2.0.0/go.mod h1:kjq9WB0s8vuUYZNYey2jp4Lgd9f4cKdzAw88FZtpj/w=
 charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU=

internal/agent/coordinator.go 🔗

@@ -361,11 +361,39 @@ func getProviderOptions(model Model, providerCfg config.ProviderConfig) fantasy.
 		if err == nil {
 			options[google.Name] = parsed
 		}
-	case openaicompat.Name:
+	case openaicompat.Name, hyper.Name:
 		_, hasReasoningEffort := mergedOptions["reasoning_effort"]
 		if !hasReasoningEffort && model.ModelCfg.ReasoningEffort != "" {
 			mergedOptions["reasoning_effort"] = model.ModelCfg.ReasoningEffort
 		}
+
+		extraBody := make(map[string]any)
+
+		// "reasoning effort" is a standard OpenAI field, but "thinking" is not.
+		// Setting it in the right way for each provider.
+		// TODO: Abstract this in Fantasy somehow?
+		// TODO: Allow custom providers to specify how to set this?
+		switch providerCfg.ID {
+		case hyper.Name:
+			extraBody["thinking"] = model.ModelCfg.Think
+		case string(catwalk.InferenceProviderIoNet):
+			extraBody["chat_template_kwargs"] = map[string]any{
+				"thinking": model.ModelCfg.Think,
+			}
+		case string(catwalk.InferenceProviderZAI):
+			if model.ModelCfg.Think {
+				extraBody["thinking"] = map[string]any{
+					"type": "enabled",
+				}
+			} else {
+				extraBody["thinking"] = map[string]any{
+					"type": "disabled",
+				}
+			}
+		}
+
+		mergedOptions["extra_body"] = extraBody
+
 		parsed, err := openaicompat.ParseOptions(mergedOptions)
 		if err == nil {
 			options[openaicompat.Name] = parsed