fix: address responses SSE review feedback
This commit is contained in:
@@ -26,13 +26,15 @@ func writeResponsesSSEChunk(w io.Writer, chunk []byte) {
|
|||||||
if w == nil || len(chunk) == 0 {
|
if w == nil || len(chunk) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _ = w.Write(chunk)
|
if _, err := w.Write(chunk); err != nil {
|
||||||
switch {
|
|
||||||
case bytes.HasSuffix(chunk, []byte("\n\n")):
|
|
||||||
return
|
return
|
||||||
case bytes.HasSuffix(chunk, []byte("\n")):
|
}
|
||||||
|
if bytes.HasSuffix(chunk, []byte("\n\n")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.HasSuffix(chunk, []byte("\n")) {
|
||||||
_, _ = w.Write([]byte("\n"))
|
_, _ = w.Write([]byte("\n"))
|
||||||
default:
|
} else {
|
||||||
_, _ = w.Write([]byte("\n\n"))
|
_, _ = w.Write([]byte("\n\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,38 +41,3 @@ func TestForwardResponsesStreamTerminalErrorUsesResponsesErrorChunk(t *testing.T
|
|||||||
t.Fatalf("expected streaming error chunk (top-level type), got HTTP error body: %q", body)
|
t.Fatalf("expected streaming error chunk (top-level type), got HTTP error body: %q", body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForwardResponsesStreamSeparatesDataOnlySSEChunks(t *testing.T) {
|
|
||||||
gin.SetMode(gin.TestMode)
|
|
||||||
base := handlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, nil)
|
|
||||||
h := NewOpenAIResponsesAPIHandler(base)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
c, _ := gin.CreateTestContext(recorder)
|
|
||||||
c.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", nil)
|
|
||||||
|
|
||||||
flusher, ok := c.Writer.(http.Flusher)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected gin writer to implement http.Flusher")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make(chan []byte, 2)
|
|
||||||
errs := make(chan *interfaces.ErrorMessage)
|
|
||||||
data <- []byte("data: {\"type\":\"response.output_item.done\",\"item\":{\"type\":\"function_call\",\"arguments\":\"{}\"}}")
|
|
||||||
data <- []byte("data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp-1\",\"output\":[]}}")
|
|
||||||
close(data)
|
|
||||||
close(errs)
|
|
||||||
|
|
||||||
h.forwardResponsesStream(c, flusher, func(error) {}, data, errs)
|
|
||||||
body := recorder.Body.String()
|
|
||||||
|
|
||||||
if !strings.Contains(body, "data: {\"type\":\"response.output_item.done\"") {
|
|
||||||
t.Fatalf("expected first SSE data chunk, got: %q", body)
|
|
||||||
}
|
|
||||||
if !strings.Contains(body, "\n\ndata: {\"type\":\"response.completed\"") {
|
|
||||||
t.Fatalf("expected blank-line separation before second SSE event, got: %q", body)
|
|
||||||
}
|
|
||||||
if strings.Contains(body, "arguments\":\"{}\"}}data: {\"type\":\"response.completed\"") {
|
|
||||||
t.Fatalf("second SSE event was concatenated onto first event body: %q", body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package openai
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||||
|
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestForwardResponsesStreamSeparatesDataOnlySSEChunks(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
base := handlers.NewBaseAPIHandlers(&sdkconfig.SDKConfig{}, nil)
|
||||||
|
h := NewOpenAIResponsesAPIHandler(base)
|
||||||
|
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(recorder)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", nil)
|
||||||
|
|
||||||
|
flusher, ok := c.Writer.(http.Flusher)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected gin writer to implement http.Flusher")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(chan []byte, 2)
|
||||||
|
errs := make(chan *interfaces.ErrorMessage)
|
||||||
|
data <- []byte("data: {\"type\":\"response.output_item.done\",\"item\":{\"type\":\"function_call\",\"arguments\":\"{}\"}}")
|
||||||
|
data <- []byte("data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp-1\",\"output\":[]}}")
|
||||||
|
close(data)
|
||||||
|
close(errs)
|
||||||
|
|
||||||
|
h.forwardResponsesStream(c, flusher, func(error) {}, data, errs)
|
||||||
|
body := recorder.Body.String()
|
||||||
|
|
||||||
|
if !strings.Contains(body, "data: {\"type\":\"response.output_item.done\"") {
|
||||||
|
t.Fatalf("expected first SSE data chunk, got: %q", body)
|
||||||
|
}
|
||||||
|
if !strings.Contains(body, "\n\ndata: {\"type\":\"response.completed\"") {
|
||||||
|
t.Fatalf("expected blank-line separation before second SSE event, got: %q", body)
|
||||||
|
}
|
||||||
|
if strings.Contains(body, "arguments\":\"{}\"}}data: {\"type\":\"response.completed\"") {
|
||||||
|
t.Fatalf("second SSE event was concatenated onto first event body: %q", body)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user