mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-04 00:15:28 +08:00
154 lines
4.3 KiB
Go
154 lines
4.3 KiB
Go
package openai
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestBuildResponseObjectToolCallsFollowChatShape(t *testing.T) {
|
|
obj := BuildResponseObject(
|
|
"resp_test",
|
|
"gpt-4o",
|
|
"prompt",
|
|
"",
|
|
`{"tool_calls":[{"name":"search","input":{"q":"golang"}}]}`,
|
|
[]string{"search"},
|
|
)
|
|
|
|
outputText, _ := obj["output_text"].(string)
|
|
if outputText != "" {
|
|
t.Fatalf("expected output_text to be hidden for tool calls, got %q", outputText)
|
|
}
|
|
|
|
output, _ := obj["output"].([]any)
|
|
if len(output) != 1 {
|
|
t.Fatalf("expected function_call output only, got %#v", obj["output"])
|
|
}
|
|
|
|
first, _ := output[0].(map[string]any)
|
|
if first["type"] != "function_call" {
|
|
t.Fatalf("expected first output item type function_call, got %#v", first["type"])
|
|
}
|
|
if first["call_id"] == "" {
|
|
t.Fatalf("expected function_call item to have call_id, got %#v", first)
|
|
}
|
|
if first["name"] != "search" {
|
|
t.Fatalf("unexpected function name: %#v", first["name"])
|
|
}
|
|
argsRaw, _ := first["arguments"].(string)
|
|
var args map[string]any
|
|
if err := json.Unmarshal([]byte(argsRaw), &args); err != nil {
|
|
t.Fatalf("arguments should be valid json string, got=%q err=%v", argsRaw, err)
|
|
}
|
|
if args["q"] != "golang" {
|
|
t.Fatalf("unexpected arguments: %#v", args)
|
|
}
|
|
}
|
|
|
|
func TestBuildResponseObjectPromotesMixedProseToolPayloadToFunctionCall(t *testing.T) {
|
|
obj := BuildResponseObject(
|
|
"resp_test",
|
|
"gpt-4o",
|
|
"prompt",
|
|
"",
|
|
`示例格式:{"tool_calls":[{"name":"search","input":{"q":"golang"}}]},但这条是普通回答。`,
|
|
[]string{"search"},
|
|
)
|
|
|
|
outputText, _ := obj["output_text"].(string)
|
|
if outputText != "" {
|
|
t.Fatalf("expected output_text hidden for mixed prose tool payload, got %q", outputText)
|
|
}
|
|
output, _ := obj["output"].([]any)
|
|
if len(output) != 1 {
|
|
t.Fatalf("expected one function_call output item, got %#v", obj["output"])
|
|
}
|
|
first, _ := output[0].(map[string]any)
|
|
if first["type"] != "function_call" {
|
|
t.Fatalf("expected function_call output type, got %#v", first["type"])
|
|
}
|
|
}
|
|
|
|
func TestBuildResponseObjectKeepsFencedToolPayloadAsText(t *testing.T) {
|
|
obj := BuildResponseObject(
|
|
"resp_test",
|
|
"gpt-4o",
|
|
"prompt",
|
|
"",
|
|
"```json\n{\"tool_calls\":[{\"name\":\"search\",\"input\":{\"q\":\"golang\"}}]}\n```",
|
|
[]string{"search"},
|
|
)
|
|
|
|
outputText, _ := obj["output_text"].(string)
|
|
if !strings.Contains(outputText, "\"tool_calls\"") {
|
|
t.Fatalf("expected output_text to preserve fenced tool payload, got %q", outputText)
|
|
}
|
|
output, _ := obj["output"].([]any)
|
|
if len(output) != 1 {
|
|
t.Fatalf("expected one message output item, got %#v", obj["output"])
|
|
}
|
|
first, _ := output[0].(map[string]any)
|
|
if first["type"] != "message" {
|
|
t.Fatalf("expected message output type, got %#v", first["type"])
|
|
}
|
|
}
|
|
|
|
// Backward-compatible alias for historical test name used in CI logs.
|
|
func TestBuildResponseObjectPromotesFencedToolPayloadToFunctionCall(t *testing.T) {
|
|
TestBuildResponseObjectKeepsFencedToolPayloadAsText(t)
|
|
}
|
|
|
|
func TestBuildResponseObjectReasoningOnlyFallsBackToOutputText(t *testing.T) {
|
|
obj := BuildResponseObject(
|
|
"resp_test",
|
|
"gpt-4o",
|
|
"prompt",
|
|
"internal thinking content",
|
|
"",
|
|
nil,
|
|
)
|
|
|
|
outputText, _ := obj["output_text"].(string)
|
|
if outputText == "" {
|
|
t.Fatalf("expected output_text fallback from reasoning when final text is empty")
|
|
}
|
|
|
|
output, _ := obj["output"].([]any)
|
|
if len(output) != 1 {
|
|
t.Fatalf("expected one output item, got %#v", obj["output"])
|
|
}
|
|
first, _ := output[0].(map[string]any)
|
|
if first["type"] != "message" {
|
|
t.Fatalf("expected output type message, got %#v", first["type"])
|
|
}
|
|
content, _ := first["content"].([]any)
|
|
if len(content) == 0 {
|
|
t.Fatalf("expected reasoning content, got %#v", first["content"])
|
|
}
|
|
block0, _ := content[0].(map[string]any)
|
|
if block0["type"] != "reasoning" {
|
|
t.Fatalf("expected first content block reasoning, got %#v", block0["type"])
|
|
}
|
|
}
|
|
|
|
func TestBuildResponseObjectIgnoresToolCallFromThinkingChannel(t *testing.T) {
|
|
obj := BuildResponseObject(
|
|
"resp_test",
|
|
"gpt-4o",
|
|
"prompt",
|
|
`{"tool_calls":[{"name":"search","input":{"q":"from-thinking"}}]}`,
|
|
"",
|
|
[]string{"search"},
|
|
)
|
|
|
|
output, _ := obj["output"].([]any)
|
|
if len(output) != 1 {
|
|
t.Fatalf("expected one message output item, got %#v", obj["output"])
|
|
}
|
|
first, _ := output[0].(map[string]any)
|
|
if first["type"] != "message" {
|
|
t.Fatalf("expected output message, got %#v", first["type"])
|
|
}
|
|
}
|