From 0afe9493878826bb3ddbd2f8b303b7c9ea8ee433 Mon Sep 17 00:00:00 2001 From: Nurwanda Romadhon Date: Wed, 29 Jul 2026 19:30:32 +0700 Subject: [PATCH] fix(antigravity): strip stream_options from non-stream requests OpenAI clients may send stream_options with stream=false; Google generateContent rejects that combination. Drop it when not streaming. --- open-sse/executors/antigravity.js | 4 ++ tests/unit/antigravity-stream-options.test.js | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/unit/antigravity-stream-options.test.js diff --git a/open-sse/executors/antigravity.js b/open-sse/executors/antigravity.js index 9ca61853..7f24fd85 100644 --- a/open-sse/executors/antigravity.js +++ b/open-sse/executors/antigravity.js @@ -136,6 +136,10 @@ export class AntigravityExecutor extends BaseExecutor { transformRequest(model, body, stream, credentials) { const projectId = credentials?.projectId || this.generateProjectId(); + // OpenAI clients may include stream_options even for non-streaming calls. + // Google generateContent rejects that combination before processing the request. + if (stream !== true) delete body.stream_options; + // ─── Image generation: completely different request structure ─── if (isImageModel(model)) { const imageConfig = parseImageConfig(model); diff --git a/tests/unit/antigravity-stream-options.test.js b/tests/unit/antigravity-stream-options.test.js new file mode 100644 index 00000000..47da8769 --- /dev/null +++ b/tests/unit/antigravity-stream-options.test.js @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; +import { AntigravityExecutor } from "../../open-sse/executors/antigravity.js"; + +const credentials = { + projectId: "synthetic-project", + connectionId: "synthetic-connection", +}; + +function requestBody(stream) { + return { + stream, + stream_options: { include_usage: true }, + request: { + contents: [{ role: "user", parts: [{ text: "Reply only OK" }] }], + }, + }; +} + +describe("AntigravityExecutor stream_options normalization", () => { + it("removes stream_options from a non-streaming request", () => { + const executor = new AntigravityExecutor(); + const output = executor.transformRequest( + "gpt-oss-120b-medium", + requestBody(false), + false, + credentials, + ); + + expect(output.stream).toBe(false); + expect(output.stream_options).toBeUndefined(); + }); + + it("preserves stream_options for a streaming request", () => { + const executor = new AntigravityExecutor(); + const output = executor.transformRequest( + "gpt-oss-120b-medium", + requestBody(true), + true, + credentials, + ); + + expect(output.stream).toBe(true); + expect(output.stream_options).toEqual({ include_usage: true }); + }); +});