Files
sub2api/backend/internal/pkg/apicompat/chatcompletions_responses_bridge_test.go
T
CHOS1N ad8afc8a2e Add parallel_tool_calls compatibility mapping
Preserve Chat Completions parallel_tool_calls when converting requests to the Responses API, and map Responses parallel_tool_calls back when falling back to Chat Completions upstreams.

Cover both true and explicit false values so clients can disable parallel tool calls without the field being dropped by omitempty.
2026-07-09 22:52:35 +08:00

157 lines
4.4 KiB
Go

package apicompat
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestResponsesInputToChatMessages_DeveloperRoleMapsToSystem(t *testing.T) {
messages, err := responsesInputToChatMessages("", json.RawMessage(`[{"role":"developer","content":"follow project instructions"}]`))
require.NoError(t, err)
require.Len(t, messages, 1)
assert.Equal(t, "system", messages[0].Role)
assert.JSONEq(t, `"follow project instructions"`, string(messages[0].Content))
}
func TestResponsesInputToChatMessages_KeepsChatCompletionRoles(t *testing.T) {
input := json.RawMessage(`[
{"role":"system","content":"system message"},
{"role":"user","content":"user message"},
{"role":"assistant","content":"assistant message"},
{"role":"tool","content":"tool message"}
]`)
messages, err := responsesInputToChatMessages("", input)
require.NoError(t, err)
require.Len(t, messages, 4)
assert.Equal(t, []string{"system", "user", "assistant", "tool"}, chatMessageRoles(messages))
}
func TestResponsesInputToChatMessages_EmptyRoleFallsBackToUser(t *testing.T) {
messages, err := responsesInputToChatMessages("", json.RawMessage(`[{"role":"","content":"hello"}]`))
require.NoError(t, err)
require.Len(t, messages, 1)
assert.Equal(t, "user", messages[0].Role)
}
func TestResponsesInputToChatMessages_DeveloperRoleTrimAndCaseInsensitive(t *testing.T) {
input := json.RawMessage(`[
{"role":" Developer ","content":"one"},
{"role":"\tDEVELOPER\n","content":"two"}
]`)
messages, err := responsesInputToChatMessages("", input)
require.NoError(t, err)
require.Len(t, messages, 2)
assert.Equal(t, []string{"system", "system"}, chatMessageRoles(messages))
}
func TestResponsesToChatCompletionsRequest_InstructionsAndInputDeveloperRole(t *testing.T) {
req := &ResponsesRequest{
Model: "gpt-4o",
Instructions: "Use concise answers.",
Input: json.RawMessage(`[
{"role":"developer","content":[{"type":"input_text","text":"Prefer JSON."}]},
{"role":"user","content":"Hello"}
]`),
}
out, err := ResponsesToChatCompletionsRequest(req)
require.NoError(t, err)
require.Len(t, out.Messages, 3)
assert.Equal(t, []string{"system", "system", "user"}, chatMessageRoles(out.Messages))
assert.JSONEq(t, `"Use concise answers."`, string(out.Messages[0].Content))
assert.JSONEq(t, `"Prefer JSON."`, string(out.Messages[1].Content))
assert.JSONEq(t, `"Hello"`, string(out.Messages[2].Content))
}
func TestResponsesToChatCompletionsRequest_TextFormatJsonObject(t *testing.T) {
req := &ResponsesRequest{
Model: "gpt-4o",
Input: json.RawMessage(`[
{"role":"user","content":"Return JSON"}
]`),
Text: &ResponsesText{
Format: json.RawMessage(`{"type":"json_object"}`),
},
}
out, err := ResponsesToChatCompletionsRequest(req)
require.NoError(t, err)
assert.JSONEq(t, `{"type":"json_object"}`, string(out.ResponseFormat))
}
func TestResponsesToChatCompletionsRequest_TextFormatJsonSchema(t *testing.T) {
req := &ResponsesRequest{
Model: "gpt-4o",
Input: json.RawMessage(`[
{"role":"user","content":"Return structured JSON"}
]`),
Text: &ResponsesText{
Format: json.RawMessage(`{
"type":"json_schema",
"name":"answer",
"schema":{
"type":"object",
"properties":{"ok":{"type":"boolean"}},
"required":["ok"],
"additionalProperties":false
},
"strict":true
}`),
},
}
out, err := ResponsesToChatCompletionsRequest(req)
require.NoError(t, err)
assert.JSONEq(t, `{
"type":"json_schema",
"json_schema":{
"name":"answer",
"schema":{
"type":"object",
"properties":{"ok":{"type":"boolean"}},
"required":["ok"],
"additionalProperties":false
},
"strict":true
}
}`, string(out.ResponseFormat))
}
func TestResponsesToChatCompletionsRequest_ParallelToolCalls(t *testing.T) {
parallel := false
req := &ResponsesRequest{
Model: "gpt-4o",
Input: json.RawMessage(`[
{"role":"user","content":"Use tools"}
]`),
ParallelToolCalls: &parallel,
}
out, err := ResponsesToChatCompletionsRequest(req)
require.NoError(t, err)
require.NotNil(t, out.ParallelToolCalls)
assert.False(t, *out.ParallelToolCalls)
payload, err := json.Marshal(out)
require.NoError(t, err)
assert.Contains(t, string(payload), `"parallel_tool_calls":false`)
}
func chatMessageRoles(messages []ChatMessage) []string {
roles := make([]string, 0, len(messages))
for _, message := range messages {
roles = append(roles, message.Role)
}
return roles
}