diff --git a/backend/internal/service/openai_gateway_grok.go b/backend/internal/service/openai_gateway_grok.go index c67a3b9919..20640daf65 100644 --- a/backend/internal/service/openai_gateway_grok.go +++ b/backend/internal/service/openai_gateway_grok.go @@ -1159,8 +1159,7 @@ func sanitizeGrokResponsesTools(body []byte) ([]byte, error) { } func grokFunctionParametersHaveInvalidUnionRoot(parameters gjson.Result) bool { - if !parameters.Exists() || !parameters.IsObject() || - strings.EqualFold(strings.TrimSpace(parameters.Get("type").String()), "object") { + if !parameters.Exists() || !parameters.IsObject() { return false } for _, keyword := range []string{"anyOf", "oneOf"} { diff --git a/backend/internal/service/openai_gateway_grok_test.go b/backend/internal/service/openai_gateway_grok_test.go index b01c14c782..d3256eac93 100644 --- a/backend/internal/service/openai_gateway_grok_test.go +++ b/backend/internal/service/openai_gateway_grok_test.go @@ -398,7 +398,7 @@ func TestSanitizeGrokResponsesToolsRemovesDeferredFlagsWithToolSearch(t *testing func TestSanitizeGrokResponsesToolsSimplifiesInvalidRootUnion(t *testing.T) { body := []byte(`{"tools":[ {"type":"function","name":"mcp__codex_app__automation_update","strict":true,"parameters":{"oneOf":[{"type":"object","properties":{"id":{"type":"string"}}},{"type":"null"}]}}, - {"type":"function","name":"object_only","strict":true,"parameters":{"anyOf":[{"type":"object","properties":{"a":{"type":"string"}}},{"type":"object","properties":{"b":{"type":"integer"}}}]}} + {"type":"function","name":"object_only","strict":true,"parameters":{"type":"object","anyOf":[{"type":"object","properties":{"a":{"type":"string"}}},{"type":"object","properties":{"b":{"type":"integer"}}}]}} ]}`) patched, err := sanitizeGrokResponsesTools(body) @@ -417,6 +417,40 @@ func TestSanitizeGrokResponsesToolsSimplifiesInvalidRootUnion(t *testing.T) { require.Equal(t, gjson.True, objectOnly.Get("strict").Type) } +func TestPatchGrokResponsesBodySimplifiesTypedInvalidRootUnion(t *testing.T) { + body := []byte(`{ + "model":"grok-4.6", + "metadata":{"session_id":"abc"}, + "tools":[{ + "type":"namespace", + "name":"mcp__codex_app", + "tools":[{ + "type":"function", + "name":"automation_update", + "strict":true, + "parameters":{ + "type":"object", + "oneOf":[{"$ref":"#/$defs/update"},{"type":"null"}], + "$defs":{"update":{"type":"object","properties":{"id":{"type":"string"}}}} + } + }] + }] + }`) + + patched, _, err := patchGrokResponsesBodyWithClientTools(body, "grok-4.6") + require.NoError(t, err) + require.True(t, json.Valid(patched)) + require.False(t, gjson.GetBytes(patched, "metadata").Exists()) + + tool := gjson.GetBytes(patched, `tools.#(name=="mcp__codex_app__automation_update")`) + require.Equal(t, "object", tool.Get("parameters.type").String()) + require.True(t, tool.Get("parameters.properties").IsObject()) + require.True(t, tool.Get("parameters.additionalProperties").Bool()) + require.False(t, tool.Get("parameters.oneOf").Exists()) + require.False(t, tool.Get("parameters.$defs").Exists()) + require.Equal(t, gjson.False, tool.Get("strict").Type) +} + func TestSanitizeGrokResponsesToolsKeepsToolChoiceOnlyWithSupportedTools(t *testing.T) { t.Parallel()