mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
Merge pull request #7162 from HuntercodeT/fix/antigravity-gemini-sse-separator
fix(antigravity): gemini 透传 SSE 事件之间不再多写一个空行(Antigravity CLI invalid stream chunk)
This commit is contained in:
@@ -1387,6 +1387,49 @@ func TestHandleClaudeStreamingResponse_NormalComplete(t *testing.T) {
|
||||
|
||||
// TestHandleGeminiStreamingResponse_ThoughtsTokenCount
|
||||
// 验证:Gemini 流式转发时 thoughtsTokenCount 被计入 OutputTokens
|
||||
// 回归:上游事件之间的空分隔行不能再透传,否则下游看到的是 "data: ...\n\n\n"。
|
||||
// google-genai Go SDK 按 "\n\n" 切事件,多出的 "\n" 会让第二个事件的前缀变成
|
||||
// "\ndata" 而报 invalid stream chunk(Antigravity CLI 每条流都在第二个事件中断)。
|
||||
func TestHandleGeminiStreamingResponse_EventSeparatorIsExactlyOneBlankLine(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
svc := newAntigravityTestService(&config.Config{
|
||||
Gateway: config.GatewayConfig{MaxLineSize: defaultMaxLineSize},
|
||||
})
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(rec)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
resp := &http.Response{StatusCode: http.StatusOK, Body: pr, Header: http.Header{}}
|
||||
|
||||
first := `{"candidates":[{"content":{"role":"model","parts":[{"text":"Hello"}]}}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":3}}`
|
||||
second := `{"candidates":[{"content":{"role":"model","parts":[{"thoughtSignature":"sig","text":""}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":3,"thoughtsTokenCount":5}}`
|
||||
|
||||
go func() {
|
||||
defer func() { _ = pw.Close() }()
|
||||
// 上游原样:每个 data 行后跟一个空分隔行(第二个事件用 CRLF,覆盖两种换行)
|
||||
fmt.Fprintf(pw, "data: %s\n\n", first)
|
||||
fmt.Fprintf(pw, "data: %s\r\n\r\n", second)
|
||||
}()
|
||||
|
||||
result, err := svc.handleGeminiStreamingResponse(c, resp, time.Now())
|
||||
_ = pr.Close()
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
body := rec.Body.String()
|
||||
require.Equal(t, "data: "+first+"\n\ndata: "+second+"\n\n", body)
|
||||
require.NotContains(t, body, "\n\n\n", "events must be separated by exactly one blank line")
|
||||
|
||||
// 模拟 google-genai 的切帧方式:按 "\n\n" 切,每个非空 token 都必须以 "data" 为前缀
|
||||
for _, token := range strings.Split(strings.TrimSuffix(body, "\n\n"), "\n\n") {
|
||||
prefix, _, _ := strings.Cut(token, ":")
|
||||
require.Equal(t, "data", prefix, "token %q would be rejected by a \\n\\n-delimited SSE parser", token)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGeminiStreamingResponse_ThoughtsTokenCount(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
svc := newAntigravityTestService(&config.Config{
|
||||
|
||||
@@ -288,6 +288,14 @@ func (s *AntigravityGatewayService) handleGeminiStreamingResponse(c *gin.Context
|
||||
continue
|
||||
}
|
||||
|
||||
// 上游每个 data 事件后面跟一个空行作为事件分隔。上面已经把 data 行写成
|
||||
// "data: ...\n\n",若再把这个空行透传出去,事件之间就会变成 "\n\n\n"。
|
||||
// google-genai 的 Go SDK(Antigravity CLI 在用)按 "\n\n" 切事件,多出的
|
||||
// "\n" 会粘到下一个事件开头,前缀变成 "\ndata" 而被判成 invalid stream chunk。
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
cw.Fprintf("%s\n", line)
|
||||
|
||||
case <-intervalCh:
|
||||
|
||||
Reference in New Issue
Block a user