mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-13 20:57:41 +08:00
127 lines
3.9 KiB
Go
127 lines
3.9 KiB
Go
package openai
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestHandleResponsesStreamToolCallsHideRawOutputTextInCompleted(t *testing.T) {
|
|
h := &Handler{}
|
|
req := httptest.NewRequest(http.MethodPost, "/v1/responses", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
sseLine := func(v string) string {
|
|
b, _ := json.Marshal(map[string]any{
|
|
"p": "response/content",
|
|
"v": v,
|
|
})
|
|
return "data: " + string(b) + "\n"
|
|
}
|
|
|
|
rawToolJSON := `{"tool_calls":[{"name":"read_file","input":{"path":"README.MD"}}]}`
|
|
streamBody := sseLine(rawToolJSON) + "data: [DONE]\n"
|
|
resp := &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(strings.NewReader(streamBody)),
|
|
}
|
|
|
|
h.handleResponsesStream(rec, req, resp, "owner-a", "resp_test", "deepseek-chat", "prompt", false, false, []string{"read_file"})
|
|
|
|
completed, ok := extractSSEEventPayload(rec.Body.String(), "response.completed")
|
|
if !ok {
|
|
t.Fatalf("expected response.completed event, body=%s", rec.Body.String())
|
|
}
|
|
responseObj, _ := completed["response"].(map[string]any)
|
|
outputText, _ := responseObj["output_text"].(string)
|
|
if outputText != "" {
|
|
t.Fatalf("expected empty output_text for tool_calls response, got output_text=%q", outputText)
|
|
}
|
|
output, _ := responseObj["output"].([]any)
|
|
if len(output) == 0 {
|
|
t.Fatalf("expected structured output entries, got %#v", responseObj["output"])
|
|
}
|
|
first, _ := output[0].(map[string]any)
|
|
if first["type"] != "tool_calls" {
|
|
t.Fatalf("expected first output type tool_calls, got %#v", first["type"])
|
|
}
|
|
toolCalls, _ := first["tool_calls"].([]any)
|
|
if len(toolCalls) == 0 {
|
|
t.Fatalf("expected at least one tool_call in output, got %#v", first["tool_calls"])
|
|
}
|
|
call0, _ := toolCalls[0].(map[string]any)
|
|
if call0["type"] != "function" {
|
|
t.Fatalf("unexpected tool call type: %#v", call0["type"])
|
|
}
|
|
fn, _ := call0["function"].(map[string]any)
|
|
if fn["name"] != "read_file" {
|
|
t.Fatalf("unexpected tool call name: %#v", fn["name"])
|
|
}
|
|
if strings.Contains(outputText, `"tool_calls"`) {
|
|
t.Fatalf("raw tool_calls JSON leaked in output_text: %q", outputText)
|
|
}
|
|
}
|
|
|
|
func TestHandleResponsesStreamIncompleteTailNotDuplicatedInCompletedOutputText(t *testing.T) {
|
|
h := &Handler{}
|
|
req := httptest.NewRequest(http.MethodPost, "/v1/responses", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
sseLine := func(v string) string {
|
|
b, _ := json.Marshal(map[string]any{
|
|
"p": "response/content",
|
|
"v": v,
|
|
})
|
|
return "data: " + string(b) + "\n"
|
|
}
|
|
|
|
tail := `{"tool_calls":[{"name":"read_file","input":`
|
|
streamBody := sseLine("Before ") + sseLine(tail) + "data: [DONE]\n"
|
|
resp := &http.Response{
|
|
StatusCode: http.StatusOK,
|
|
Body: io.NopCloser(strings.NewReader(streamBody)),
|
|
}
|
|
|
|
h.handleResponsesStream(rec, req, resp, "owner-a", "resp_test", "deepseek-chat", "prompt", false, false, []string{"read_file"})
|
|
|
|
completed, ok := extractSSEEventPayload(rec.Body.String(), "response.completed")
|
|
if !ok {
|
|
t.Fatalf("expected response.completed event, body=%s", rec.Body.String())
|
|
}
|
|
responseObj, _ := completed["response"].(map[string]any)
|
|
outputText, _ := responseObj["output_text"].(string)
|
|
if strings.Count(outputText, tail) > 1 {
|
|
t.Fatalf("expected incomplete tail not to be duplicated, got output_text=%q", outputText)
|
|
}
|
|
}
|
|
|
|
func extractSSEEventPayload(body, targetEvent string) (map[string]any, bool) {
|
|
scanner := bufio.NewScanner(strings.NewReader(body))
|
|
matched := false
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if strings.HasPrefix(line, "event: ") {
|
|
evt := strings.TrimSpace(strings.TrimPrefix(line, "event: "))
|
|
matched = evt == targetEvent
|
|
continue
|
|
}
|
|
if !matched || !strings.HasPrefix(line, "data: ") {
|
|
continue
|
|
}
|
|
raw := strings.TrimSpace(strings.TrimPrefix(line, "data: "))
|
|
if raw == "" || raw == "[DONE]" {
|
|
continue
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
|
|
return nil, false
|
|
}
|
|
return payload, true
|
|
}
|
|
return nil, false
|
|
}
|