fix(openai): OAuth 透传路径补齐 input 规范化,修复 Input must be a list

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:<text>}]
- 空白 string → []
- 单 object → [<object>]
This commit is contained in:
li
2026-07-22 16:46:29 +08:00
parent d0bdd7e771
commit 851436c552
2 changed files with 82 additions and 0 deletions
@@ -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")
@@ -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())
}