#7277·sub2api

Antigravity 账号 Gemini 原生流的 ":" 注释心跳会让 go-genai / python-genai 客户端报 invalid stream chunk 中断

Author: HuntercodeTCreated Sep 17, 2026Updated Sep 17, 2026

现象

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.gohandleGeminiStreamingResponse 在空闲时向下游写 SSE 注释行 ":\n\n" 作为心跳(防止 Cloudflare Tunnel 等代理断开)。

SSE 规范允许注释行,但 Google 官方 SDK 里有两个实现不忽略它:

  • go-genaiapi_client.go iterateResponseStream 对任何非 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 原生流不发注释心跳;其他客户端行为不变。