fix: Remove redundant text accumulation to prevent duplicate output in streamed responses and add a test for it.

This commit is contained in:
CJACK
2026-02-19 00:08:03 +08:00
parent 2dcc230852
commit df9aea194c
2 changed files with 70 additions and 1 deletions

View File

@@ -159,7 +159,6 @@ func (h *Handler) handleResponsesStream(w http.ResponseWriter, r *http.Request,
if bufferToolContent {
for _, evt := range flushToolSieve(&sieve, toolNames) {
if evt.Content != "" {
finalText += evt.Content
sendEvent("response.output_text.delta", util.BuildOpenAIResponsesTextDeltaPayload(responseID, evt.Content))
}
if len(evt.ToolCalls) > 0 {

View File

@@ -0,0 +1,70 @@
package openai
import (
"bufio"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestHandleResponsesStreamNoDuplicateTailInCompletedOutputText(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 tail to appear once in output_text, 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
}