Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion open-sse/config/cliFingerprints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
5 changes: 4 additions & 1 deletion open-sse/executors/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -582,6 +582,8 @@ export class BaseExecutor {
if (cloned[key] === "") delete cloned[key];
}

stripInternalBodyFields(cloned);

return cloned;
}

Expand Down Expand Up @@ -1393,6 +1395,7 @@ export class BaseExecutor {
);
}

stripInternalBodyFields(transformedBody);
let bodyString = JSON.stringify(transformedBody);

const shouldFingerprint =
Expand Down
7 changes: 6 additions & 1 deletion open-sse/handlers/chatCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2804,7 +2804,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.
Expand Down
15 changes: 14 additions & 1 deletion open-sse/handlers/chatCore/agentRouterProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
120 changes: 120 additions & 0 deletions tests/unit/strip-store-responses.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
Loading