fix(sse): preserve store parameter semantics for openai-compatible responses (#11826) (#11916)

stripStore() now forces store=false for stateless OpenAI-compatible Responses-API targets unless the connection explicitly opts in via providerSpecificData.openaiStoreEnabled, instead of only handling the openai/agentrouter cases — a client-supplied store value previously passed through untouched to backends that don't actually persist responses server-side. Closes #11826. Thanks!
This commit is contained in:
Bob.Hou
2026-08-28 15:25:19 -04:00
committed by GitHub
parent dc75a02ca7
commit 6b259812a7
5 changed files with 145 additions and 4 deletions

View File

@@ -265,7 +265,7 @@ export function orderHeaders(
* Apply a CLI fingerprint to headers and body.
* Returns { headers, bodyString } with the correct ordering.
*/
function stripInternalBodyFields(body: unknown): unknown {
export function stripInternalBodyFields(body: unknown): unknown {
if (!body || typeof body !== "object" || Array.isArray(body)) return body;
const record = body as Record<string, unknown>;

View File

@@ -30,7 +30,7 @@ import {
addParamToBlocklist,
isAutoLearnGloballyEnabled,
} from "@/lib/db/paramFilters";
import { applyFingerprint, isCliCompatEnabled } from "../config/cliFingerprints.ts";
import { applyFingerprint, isCliCompatEnabled, stripInternalBodyFields } from "../config/cliFingerprints.ts";
import { supportsClaudeMaxEffort, supportsXHighEffort } from "../config/providerModels.ts";
import { getThinkingBudgetConfig, ThinkingMode } from "../services/thinkingBudget.ts";
import {
@@ -582,6 +582,8 @@ export class BaseExecutor {
if (cloned[key] === "") delete cloned[key];
}
stripInternalBodyFields(cloned);
return cloned;
}
@@ -1393,6 +1395,7 @@ export class BaseExecutor {
);
}
stripInternalBodyFields(transformedBody);
let bodyString = JSON.stringify(transformedBody);
const shouldFingerprint =

View File

@@ -2808,7 +2808,12 @@ export async function handleChatCore({
log?.debug?.("PARAMS", `Renamed max_completion_tokens to max_tokens for ${model}`);
}
stripStore(translatedBody, provider, targetFormat);
stripStore(
translatedBody,
provider,
targetFormat,
credentials?.providerSpecificData as Record<string, unknown> | null | undefined
);
// Chat clients may send stream_options.include_usage, but OpenAI Responses
// upstreams (including Azure AI Foundry /responses) reject stream_options.

View File

@@ -26,8 +26,21 @@ export function usesClaudeBridge(
export function stripStore(
body: Record<string, unknown>,
provider: string,
targetFormat: string
targetFormat: string,
providerSpecificData?: unknown
): void {
if (provider.startsWith("openai-compatible-") && targetFormat === FORMATS.OPENAI_RESPONSES) {
const psd =
providerSpecificData && typeof providerSpecificData === "object"
? (providerSpecificData as Record<string, unknown>)
: undefined;
if (psd?.openaiStoreEnabled === true) {
return;
}
body.store = false;
return;
}
const supportsStore =
provider === "openai" ||
(provider === "agentrouter" && targetFormat === FORMATS.OPENAI_RESPONSES);

View File

@@ -0,0 +1,120 @@
import assert from "node:assert/strict";
import { createServer } from "node:http";
import test from "node:test";
import { DefaultExecutor } from "../../open-sse/executors/default.ts";
import { stripStore } from "../../open-sse/handlers/chatCore/agentRouterProtocol.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
const COMPATIBLE_PROVIDER = "openai-compatible-responses-test";
test("stripStore forces store=false for stateless OpenAI-compatible Responses requests", () => {
for (const initialStore of [undefined, false, true]) {
const body: Record<string, unknown> = {};
if (initialStore !== undefined) body.store = initialStore;
stripStore(body, COMPATIBLE_PROVIDER, FORMATS.OPENAI_RESPONSES, {});
assert.equal(body.store, false);
}
});
test("stripStore preserves client store values for opted-in OpenAI-compatible Responses requests", () => {
for (const initialStore of [false, true]) {
const body: Record<string, unknown> = { store: initialStore };
stripStore(body, COMPATIBLE_PROVIDER, FORMATS.OPENAI_RESPONSES, {
openaiStoreEnabled: true,
});
assert.equal(body.store, initialStore);
}
const omitted: Record<string, unknown> = {};
stripStore(omitted, COMPATIBLE_PROVIDER, FORMATS.OPENAI_RESPONSES, {
openaiStoreEnabled: true,
});
assert.equal("store" in omitted, false);
});
test("stripStore keeps existing OpenAI and AgentRouter behavior", () => {
const cases = [
{ provider: "openai", targetFormat: FORMATS.OPENAI, expected: true },
{ provider: "openai", targetFormat: FORMATS.OPENAI_RESPONSES, expected: true },
{ provider: "agentrouter", targetFormat: FORMATS.OPENAI_RESPONSES, expected: true },
{ provider: "agentrouter", targetFormat: FORMATS.OPENAI, expected: false },
];
for (const { provider, targetFormat, expected } of cases) {
const body: Record<string, unknown> = { store: true };
stripStore(body, provider, targetFormat, {});
assert.equal("store" in body, expected, `${provider}/${targetFormat}`);
}
});
test("stripStore removes store outside OpenAI-compatible Responses targets", () => {
const cases = [
{ provider: COMPATIBLE_PROVIDER, targetFormat: FORMATS.OPENAI },
{ provider: "anthropic", targetFormat: FORMATS.CLAUDE },
];
for (const { provider, targetFormat } of cases) {
const body: Record<string, unknown> = { store: false };
stripStore(body, provider, targetFormat, { openaiStoreEnabled: true });
assert.equal("store" in body, false, `${provider}/${targetFormat}`);
}
});
test("DefaultExecutor never serializes native passthrough markers upstream", async () => {
let capturedBody: Record<string, unknown> | null = null;
const server = createServer((request, response) => {
let rawBody = "";
request.setEncoding("utf8");
request.on("data", (chunk) => {
rawBody += chunk;
});
request.on("end", () => {
capturedBody = JSON.parse(rawBody);
response.writeHead(200, { "Content-Type": "application/json" });
response.end(JSON.stringify({ id: "resp_test", object: "response", output: [] }));
});
});
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
const address = server.address();
assert.ok(address && typeof address !== "string");
try {
const executor = new DefaultExecutor(COMPATIBLE_PROVIDER);
await executor.execute({
model: "gpt-5.6-test",
body: {
model: "gpt-5.6-test",
input: "hi",
store: false,
_nativeOpenAICompatibleResponsesPassthrough: true,
_nativeCodexPassthrough: true,
_nativeXaiResponsesPassthrough: true,
_omnirouteResponsesStore: false,
},
stream: false,
credentials: {
apiKey: "test-key",
providerSpecificData: {
apiType: "responses",
baseUrl: `http://127.0.0.1:${address.port}/v1`,
},
},
});
} finally {
await new Promise<void>((resolve, reject) =>
server.close((error) => (error ? reject(error) : resolve()))
);
}
assert.ok(capturedBody);
assert.equal(capturedBody.store, false);
assert.equal(capturedBody._nativeOpenAICompatibleResponsesPassthrough, undefined);
assert.equal(capturedBody._nativeCodexPassthrough, undefined);
assert.equal(capturedBody._nativeXaiResponsesPassthrough, undefined);
assert.equal(capturedBody._omnirouteResponsesStore, undefined);
});