diff --git a/backend/internal/service/openai_gateway_request_body.go b/backend/internal/service/openai_gateway_request_body.go index b47c2c8638..d2846c59a0 100644 --- a/backend/internal/service/openai_gateway_request_body.go +++ b/backend/internal/service/openai_gateway_request_body.go @@ -575,6 +575,34 @@ func normalizeOpenAIPassthroughOAuthBody(body []byte, compact bool) ([]byte, boo changed = true } + if inputResult := gjson.GetBytes(normalized, "input"); inputResult.Exists() { + switch { + case inputResult.Type == gjson.String: + text := inputResult.String() + var inputValue any + if strings.TrimSpace(text) != "" { + inputValue = []map[string]any{{ + "type": "message", "role": "user", "content": text, + }} + } else { + inputValue = []any{} + } + next, err := sjson.SetBytes(normalized, "input", inputValue) + if err != nil { + return body, false, fmt.Errorf("normalize passthrough body input string: %w", err) + } + normalized = next + changed = true + case inputResult.Type == gjson.JSON && !inputResult.IsArray(): + next, err := sjson.SetRawBytes(normalized, "input", []byte("["+inputResult.Raw+"]")) + if err != nil { + return body, false, fmt.Errorf("normalize passthrough body input object: %w", err) + } + normalized = next + changed = true + } + } + if compact { if store := gjson.GetBytes(normalized, "store"); store.Exists() { next, err := sjson.DeleteBytes(normalized, "store") diff --git a/backend/internal/service/openai_passthrough_normalization_test.go b/backend/internal/service/openai_passthrough_normalization_test.go index 492ff61057..0212c3e1f8 100644 --- a/backend/internal/service/openai_passthrough_normalization_test.go +++ b/backend/internal/service/openai_passthrough_normalization_test.go @@ -31,3 +31,57 @@ func TestNormalizeOpenAIPassthroughOAuthBody_CompactRemovesUnsupportedUser(t *te require.False(t, gjson.GetBytes(normalized, "stream").Exists()) require.False(t, gjson.GetBytes(normalized, "store").Exists()) } + +func TestNormalizeOpenAIPassthroughOAuthBody_StringInputWrappedAsArray(t *testing.T) { + body := []byte(`{"model":"gpt-5.4","input":"hello world"}`) + + normalized, changed, err := normalizeOpenAIPassthroughOAuthBody(body, false) + require.NoError(t, err) + require.True(t, changed) + + input := gjson.GetBytes(normalized, "input") + require.True(t, input.IsArray(), "string input should be converted to array") + items := input.Array() + require.Len(t, items, 1) + require.Equal(t, "message", items[0].Get("type").String()) + require.Equal(t, "user", items[0].Get("role").String()) + require.Equal(t, "hello world", items[0].Get("content").String()) +} + +func TestNormalizeOpenAIPassthroughOAuthBody_EmptyStringInputWrappedAsEmptyArray(t *testing.T) { + body := []byte(`{"model":"gpt-5.4","input":" "}`) + + normalized, changed, err := normalizeOpenAIPassthroughOAuthBody(body, false) + require.NoError(t, err) + require.True(t, changed) + + input := gjson.GetBytes(normalized, "input") + require.True(t, input.IsArray()) + require.Len(t, input.Array(), 0, "whitespace-only input should become empty array") +} + +func TestNormalizeOpenAIPassthroughOAuthBody_ObjectInputWrappedAsArray(t *testing.T) { + body := []byte(`{"model":"gpt-5.4","input":{"type":"message","role":"user","content":"hi"}}`) + + normalized, changed, err := normalizeOpenAIPassthroughOAuthBody(body, false) + require.NoError(t, err) + require.True(t, changed) + + input := gjson.GetBytes(normalized, "input") + require.True(t, input.IsArray(), "object input should be wrapped in array") + items := input.Array() + require.Len(t, items, 1) + require.Equal(t, "message", items[0].Get("type").String()) +} + +func TestNormalizeOpenAIPassthroughOAuthBody_ArrayInputUnchanged(t *testing.T) { + body := []byte(`{"model":"gpt-5.4","input":[{"type":"message","role":"user","content":"hi"}]}`) + + normalized, _, err := normalizeOpenAIPassthroughOAuthBody(body, false) + require.NoError(t, err) + + input := gjson.GetBytes(normalized, "input") + require.True(t, input.IsArray()) + require.Len(t, input.Array(), 1) + require.Equal(t, "message", input.Array()[0].Get("type").String()) +}