From 851436c552bdc003d4a1b1affa13b19855494b8d Mon Sep 17 00:00:00 2001 From: li Date: Wed, 22 Jul 2026 16:46:29 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(openai):=20OAuth=20=E9=80=8F=E4=BC=A0?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E8=A1=A5=E9=BD=90=20input=20=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E5=8C=96=EF=BC=8C=E4=BF=AE=E5=A4=8D=20Input=20must=20?= =?UTF-8?q?be=20a=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openai_passthrough=true 的 OAuth 账号透传路径 (normalizeOpenAIPassthroughOAuthBody) 未对 input 字段做 string→array 转换,导致上游拒绝 "Input must be a list"。 正常 OAuth 路径 (openai_codex_transform.go:290) 已有此逻辑, 透传路径遗漏。补齐三种非数组 input 的规范化: - string → [{type:"message",role:"user",content:}] - 空白 string → [] - 单 object → [] --- .../service/openai_gateway_request_body.go | 28 ++++++++++ .../openai_passthrough_normalization_test.go | 54 +++++++++++++++++++ 2 files changed, 82 insertions(+) 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()) +} From 3e26dfa5bbf4b4a7d1d2287a9488926b9ea3e256 Mon Sep 17 00:00:00 2001 From: li Date: Wed, 22 Jul 2026 16:49:31 +0800 Subject: [PATCH 2/2] =?UTF-8?q?style:=20=E7=BB=9F=E4=B8=80=20input=20?= =?UTF-8?q?=E5=8C=85=E8=A3=85=E7=B1=BB=E5=9E=8B=E4=B8=BA=20[]any=EF=BC=8C?= =?UTF-8?q?=E4=B8=8E=E6=AD=A3=E5=B8=B8=20OAuth=20=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/service/openai_gateway_request_body.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/internal/service/openai_gateway_request_body.go b/backend/internal/service/openai_gateway_request_body.go index d2846c59a0..14520958f2 100644 --- a/backend/internal/service/openai_gateway_request_body.go +++ b/backend/internal/service/openai_gateway_request_body.go @@ -581,7 +581,7 @@ func normalizeOpenAIPassthroughOAuthBody(body []byte, compact bool) ([]byte, boo text := inputResult.String() var inputValue any if strings.TrimSpace(text) != "" { - inputValue = []map[string]any{{ + inputValue = []any{map[string]any{ "type": "message", "role": "user", "content": text, }} } else {