Merge pull request #2528 from wucm667/fix/openai-responses-null-content
fix(openai): 修复 chat-completions 转 responses 时 content 为 null 导致上游 400
This commit is contained in:
commit
23e95b77b7
@ -225,6 +225,41 @@ func TestChatCompletionsToResponses_WhitespaceOnlyBase64ImageURLSkipped(t *testi
|
||||
assert.Equal(t, "Describe this", parts[0].Text)
|
||||
}
|
||||
|
||||
func TestChatCompletionsToResponses_EmptyContentNeverNull(t *testing.T) {
|
||||
// Regression for #2515: the upstream Responses API rejects an input item
|
||||
// whose content field is JSON null. Any chat-completions message that
|
||||
// yields no usable content parts must serialize content as a string.
|
||||
cases := []struct {
|
||||
name string
|
||||
content json.RawMessage
|
||||
}{
|
||||
{"null content", json.RawMessage(`null`)},
|
||||
{"empty array content", json.RawMessage(`[]`)},
|
||||
{"only empty text part", json.RawMessage(`[{"type":"text","text":""}]`)},
|
||||
{"only empty base64 image part", json.RawMessage(`[{"type":"image_url","image_url":{"url":"data:image/png;base64,"}}]`)},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req := &ChatCompletionsRequest{
|
||||
Model: "gpt-5.5",
|
||||
Messages: []ChatMessage{
|
||||
{Role: "user", Content: tc.content},
|
||||
},
|
||||
}
|
||||
resp, err := ChatCompletionsToResponses(req)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, string(resp.Input), `"content":null`,
|
||||
"converted input must not contain a null content field")
|
||||
|
||||
var items []ResponsesInputItem
|
||||
require.NoError(t, json.Unmarshal(resp.Input, &items))
|
||||
require.Len(t, items, 1)
|
||||
assert.Equal(t, `""`, string(items[0].Content),
|
||||
"content must be an empty string, not null")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatCompletionsToResponses_SystemArrayContent(t *testing.T) {
|
||||
req := &ChatCompletionsRequest{
|
||||
Model: "gpt-4o",
|
||||
|
||||
@ -325,7 +325,14 @@ func marshalChatInputContent(content chatMessageContent) (json.RawMessage, error
|
||||
if content.Text != nil {
|
||||
return json.Marshal(*content.Text)
|
||||
}
|
||||
return json.Marshal(convertChatContentPartsToResponses(content.Parts))
|
||||
parts := convertChatContentPartsToResponses(content.Parts)
|
||||
if len(parts) == 0 {
|
||||
// A nil slice marshals to JSON null, which the upstream Responses API
|
||||
// rejects ("expected an array of objects or string, but got null").
|
||||
// Fall back to an empty string when no usable parts remain.
|
||||
return json.Marshal("")
|
||||
}
|
||||
return json.Marshal(parts)
|
||||
}
|
||||
|
||||
func convertChatContentPartsToResponses(parts []ChatContentPart) []ResponsesContentPart {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user