diff --git a/backend/internal/handler/gateway_handler.go b/backend/internal/handler/gateway_handler.go index d07cd57e13..02e84fdcae 100644 --- a/backend/internal/handler/gateway_handler.go +++ b/backend/internal/handler/gateway_handler.go @@ -1307,11 +1307,15 @@ func writeGrokModelsList(c *gin.Context, modelIDs []string) { if grokModelSupportsConfigurableReasoning(modelID) { item.SupportsReasoningEffort = true item.ReasoningEffort = "high" - item.ReasoningEfforts = []grokReasoningEffortOption{ + efforts := []grokReasoningEffortOption{ {Value: "low", Label: "Low"}, {Value: "medium", Label: "Medium"}, {Value: "high", Label: "High", Default: true}, } + if service.GrokSupportsXHighReasoningEffort(modelID) { + efforts = append(efforts, grokReasoningEffortOption{Value: "xhigh", Label: "xHigh"}) + } + item.ReasoningEfforts = efforts } models = append(models, item) } diff --git a/backend/internal/handler/gateway_models_test.go b/backend/internal/handler/gateway_models_test.go index 05069bf25a..8059611faa 100644 --- a/backend/internal/handler/gateway_models_test.go +++ b/backend/internal/handler/gateway_models_test.go @@ -364,9 +364,38 @@ func TestGatewayModels_GeminiGroupFallsBackToGeminiModels(t *testing.T) { } func TestGatewayModels_Grok45AdvertisesReasoningEffortForGrokBuild(t *testing.T) { + assertGrokGatewayReasoningEfforts(t, 4409, "grok-4.5", []gatewayReasoningEffortOptionForTest{ + {Value: "low", Label: "Low"}, + {Value: "medium", Label: "Medium"}, + {Value: "high", Label: "High", Default: true}, + }) +} + +func TestGatewayModels_Grok46AdvertisesXHighReasoningEffortForGrokBuild(t *testing.T) { + xhighEfforts := []gatewayReasoningEffortOptionForTest{ + {Value: "low", Label: "Low"}, + {Value: "medium", Label: "Medium"}, + {Value: "high", Label: "High", Default: true}, + {Value: "xhigh", Label: "xHigh"}, + } + tests := []struct { + groupID int64 + model string + }{ + {groupID: 4410, model: "grok-4.6"}, + {groupID: 4411, model: "grok-4.6-latest"}, + } + for _, tt := range tests { + t.Run(tt.model, func(t *testing.T) { + assertGrokGatewayReasoningEfforts(t, tt.groupID, tt.model, xhighEfforts) + }) + } +} + +func assertGrokGatewayReasoningEfforts(t *testing.T, groupID int64, modelID string, want []gatewayReasoningEffortOptionForTest) { + t.Helper() gin.SetMode(gin.TestMode) - groupID := int64(4409) h := newGatewayModelsHandlerForTest( &gatewayModelsAccountRepoStub{ byGroup: map[int64][]service.Account{ @@ -375,7 +404,7 @@ func TestGatewayModels_Grok45AdvertisesReasoningEffortForGrokBuild(t *testing.T) ID: 1, Platform: service.PlatformGrok, Credentials: map[string]any{ - "model_mapping": map[string]any{"grok-4.5": "grok-4.5"}, + "model_mapping": map[string]any{modelID: modelID}, }, }, }, @@ -397,14 +426,10 @@ func TestGatewayModels_Grok45AdvertisesReasoningEffortForGrokBuild(t *testing.T) require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got)) require.Len(t, got.Data, 1) model := got.Data[0] - require.Equal(t, "grok-4.5", model.ID) + require.Equal(t, modelID, model.ID) require.True(t, model.SupportsReasoningEffort) require.Equal(t, "high", model.ReasoningEffort) - require.Equal(t, []gatewayReasoningEffortOptionForTest{ - {Value: "low", Label: "Low"}, - {Value: "medium", Label: "Medium"}, - {Value: "high", Label: "High", Default: true}, - }, model.ReasoningEfforts) + require.Equal(t, want, model.ReasoningEfforts) } func TestGatewayModels_GeminiGroupFiltersMappedModelsByPlatform(t *testing.T) { diff --git a/backend/internal/service/openai_codex_models_service.go b/backend/internal/service/openai_codex_models_service.go index 95be9ebbaf..b55549e753 100644 --- a/backend/internal/service/openai_codex_models_service.go +++ b/backend/internal/service/openai_codex_models_service.go @@ -477,7 +477,7 @@ func configuredCodexGrokReasoningLevels(modelID string) []configuredCodexReasoni {Effort: "medium", Description: "Balanced reasoning for most coding tasks"}, {Effort: "high", Description: "Greater reasoning depth for coding and agent tasks"}, } - if grokSupportsXHighReasoningEffort(modelID) { + if GrokSupportsXHighReasoningEffort(modelID) { levels = append(levels, configuredCodexReasoningLevel{ Effort: "xhigh", Description: "Extra-high reasoning depth for difficult tasks", diff --git a/backend/internal/service/openai_gateway_grok.go b/backend/internal/service/openai_gateway_grok.go index 21c7342356..a99f37ac2a 100644 --- a/backend/internal/service/openai_gateway_grok.go +++ b/backend/internal/service/openai_gateway_grok.go @@ -723,7 +723,7 @@ func normalizeGrokReasoningEffortValue(raw, model string) (string, bool) { case "minimal": return "low", true case "xhigh", "extrahigh": - if grokSupportsXHighReasoningEffort(model) { + if GrokSupportsXHighReasoningEffort(model) { return "xhigh", true } return "high", true @@ -734,7 +734,9 @@ func normalizeGrokReasoningEffortValue(raw, model string) (string, bool) { } } -func grokSupportsXHighReasoningEffort(model string) bool { +// GrokSupportsXHighReasoningEffort reports whether the model advertises and +// forwards the xhigh reasoning effort (Grok 4.6 and its undated alias). +func GrokSupportsXHighReasoningEffort(model string) bool { model = strings.ToLower(xai.StripGrokProviderPrefix(strings.TrimSpace(model))) return model == "grok-4.6" || model == "grok-4.6-latest" }