Antigravity 账号 Gemini 原生流的 ":" 注释心跳会让 go-genai / python-genai 客户端报 invalid stream chunk 中断
现象
Antigravity CLI(agy 1.2.5,底层 go-genai 1.71.0)经 sub2api 0.2.5 网关(Antigravity 账号,Gemini 原生 /v1beta/models/*:streamGenerateContent?alt=sse)跑长思考请求时,客户端偶发直接中断:
⚠ agent executor error: generating and executing: iterateResponseStream: invalid stream chunk: :触发条件:上游连续 gateway.stream_keepalive_interval(默认 10s)没有任何 chunk。短请求复现不了,因为 thinking 片段通常几秒内就到;上游排队或长上下文首 chunk 慢于 10s 时必现。
根因
backend/internal/service/antigravity_gateway_streaming.go 的 handleGeminiStreamingResponse 在空闲时向下游写 SSE 注释行 ":\n\n" 作为心跳(防止 Cloudflare Tunnel 等代理断开)。
SSE 规范允许注释行,但 Google 官方 SDK 里有两个实现不忽略它:
- go-genai:
api_client.goiterateResponseStream对任何非data:前缀的事件直接返回invalid stream chunk: <prefix>:<data>,注释行:就变成报错里的: :,整条流中断。 - python-genai:
_api_client.py把非data:行当作「错误 JSON」按花括号配平拼接后交给json.loads,:同样抛错。
js-genai 用的是规范的 event-stream 解析器,能正确忽略注释。Google 官方 API 从不下发注释行,所以这两个 SDK 从未处理过这种输入。
同一文件里 Claude 格式的流用的是 event: ping\ndata: {"type": "ping"} 事件,客户端能处理;只有 Gemini 原生流没有可用的「数据形态心跳」。
为什么不能靠配置绕开
gateway.stream_keepalive_interval 是全局的(OpenAI passthrough / chat completions / Anthropic passthrough / images 都用),设 0 会把其他路径的心跳一起关掉;合法值只有 0 或 5–30,调到 30 只是降低概率。
修复
见 PR:按下游 User-Agent / X-Goog-Api-Client 识别 google-genai-sdk/… gl-go/… 与 gl-python/…,命中时 Gemini 原生流不发注释心跳;其他客户端行为不变。
Source: Wei-Shaw/sub2api