style: fix lint errors in response.failed SSE writer
Errcheck flagged three unchecked strings.Builder.WriteString calls and gofmt rejected over-aligned trailing comment in the route table. Rewrite writeResponsesFailedSSE with json.Marshal on typed structs instead of Builder+strconv.Quote. Same wire format, but: - no unchecked Write returns to silence - strict JSON escaping (strconv.Quote emits \a and \v which are not valid JSON; Marshal handles all runes correctly) - omitempty model field via struct tag instead of conditional Builder - consistent with the json.Marshal style used elsewhere in handler/ Collapse trailing comment whitespace in stream_error_event_test.go to satisfy gofmt. All 30+ subtests in the package still pass.
This commit is contained in:
parent
a18738b29e
commit
53acde1efd
@ -1,9 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey"
|
||||
@ -11,6 +11,30 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// responsesFailedError 对齐 OpenAI Responses 协议 error 子对象。
|
||||
type responsesFailedError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// responsesFailedBody 对齐 apicompat.makeResponsesCompletedEvent 输出的 response 子对象字段集。
|
||||
// Output 用空 slice(不是 nil)确保 marshal 为 `[]` 而非 `null`。
|
||||
type responsesFailedBody struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Output []any `json:"output"`
|
||||
Error responsesFailedError `json:"error"`
|
||||
}
|
||||
|
||||
// responsesFailedEvent 是写入 SSE data 行的顶层结构。
|
||||
// 故意不带 sequence_number:spec 标记可选,且本函数被调用时无法可靠拿到 last seq。
|
||||
type responsesFailedEvent struct {
|
||||
Type string `json:"type"`
|
||||
Response responsesFailedBody `json:"response"`
|
||||
}
|
||||
|
||||
// writeResponsesFailedSSE emits a `response.failed` SSE event in the OpenAI
|
||||
// Responses API protocol after the stream has already started.
|
||||
//
|
||||
@ -33,27 +57,27 @@ func writeResponsesFailedSSE(c *gin.Context, errType, message string) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
rid := synthesizeResponseID(c)
|
||||
model := requestModel(c)
|
||||
code := mapResponsesErrorCode(errType)
|
||||
|
||||
var b strings.Builder
|
||||
b.Grow(256 + len(message) + len(model))
|
||||
b.WriteString(`{"type":"response.failed","response":{`)
|
||||
b.WriteString(`"id":`)
|
||||
b.WriteString(strconv.Quote(rid))
|
||||
b.WriteString(`,"object":"response"`)
|
||||
if model != "" {
|
||||
b.WriteString(`,"model":`)
|
||||
b.WriteString(strconv.Quote(model))
|
||||
payload, err := json.Marshal(responsesFailedEvent{
|
||||
Type: "response.failed",
|
||||
Response: responsesFailedBody{
|
||||
ID: synthesizeResponseID(c),
|
||||
Object: "response",
|
||||
Model: requestModel(c),
|
||||
Status: "failed",
|
||||
Output: []any{},
|
||||
Error: responsesFailedError{
|
||||
Code: mapResponsesErrorCode(errType),
|
||||
Message: message,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
_ = c.Error(err)
|
||||
return true
|
||||
}
|
||||
b.WriteString(`,"status":"failed","output":[],"error":{"code":`)
|
||||
b.WriteString(strconv.Quote(code))
|
||||
b.WriteString(`,"message":`)
|
||||
b.WriteString(strconv.Quote(message))
|
||||
b.WriteString(`}}}`)
|
||||
|
||||
if _, err := fmt.Fprintf(c.Writer, "event: response.failed\ndata: %s\n\n", b.String()); err != nil {
|
||||
if _, err := fmt.Fprintf(c.Writer, "event: response.failed\ndata: %s\n\n", payload); err != nil {
|
||||
_ = c.Error(err)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ func TestInboundIsResponses_CoversAllRoutes(t *testing.T) {
|
||||
}{
|
||||
{"/v1/responses", true},
|
||||
{"/v1/responses/compact", true},
|
||||
{"/responses", true}, // <-- 用户 16 实际走这条
|
||||
{"/responses", true}, // <-- 用户 16 实际走这条
|
||||
{"/responses/compact", true},
|
||||
{"/backend-api/codex/responses", true},
|
||||
{"/backend-api/codex/responses/compact", true},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user