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
54 changes: 54 additions & 0 deletions open-sse/executors/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,50 @@ export function applyConfiguredUserAgent(
}
}

/**
* Returns true when the outbound request targets an OpenAI-compatible endpoint
* (a `openai-compatible-*` provider, or a Chat Completions / Responses URL).
* Used to scope the X-Stainless strip narrowly so genuine SDK-spoofing paths
* (e.g. Claude Code compat, which legitimately ADDS X-Stainless-*) are untouched.
*/
export function isOpenAICompatibleEndpoint(provider: string, url: string): boolean {
if (provider?.startsWith?.("openai-compatible-")) return true;
return url.includes("/v1/chat/completions") || url.includes("/v1/responses");
}

/**
* Strip OpenAI SDK (`X-Stainless-*`) metadata headers and normalize an SDK-derived
* User-Agent for OpenAI-compatible passthrough requests. Some upstream gateways
* 403 on these SDK-identifying headers. Only applied to OpenAI-compatible endpoints —
* other providers (Claude/Claude Code compat) may legitimately send X-Stainless-*.
*
* Mutates `headers` in place and returns the list of stripped header keys (for logging).
*/
export function stripStainlessHeadersForOpenAICompat(
headers: Record<string, string>,
provider: string,
url: string
): string[] {
if (!isOpenAICompatibleEndpoint(provider, url)) return [];

const strippedKeys: string[] = [];
for (const key of Object.keys(headers)) {
if (key.toLowerCase().startsWith("x-stainless-")) {
delete headers[key];
strippedKeys.push(key);
}
}

// Normalize User-Agent: SDK-based clients send verbose product strings that some
// upstreams block. Replace with a clean browser-like UA only when it looks SDK-derived.
const ua = (headers["User-Agent"] || headers["user-agent"] || "").toLowerCase();
if (ua.includes("openai") && (ua.includes("node") || ua.includes("axios") || ua.includes("undici"))) {
setUserAgentHeader(headers, "Mozilla/5.0 (compatible; OpenAI Compatible)");
}

return strippedKeys;
}

export function mergeAbortSignals(primary: AbortSignal, secondary: AbortSignal): AbortSignal {
const controller = new AbortController();

Expand Down Expand Up @@ -817,6 +861,16 @@ export class BaseExecutor {
const headers = this.buildHeaders(activeCredentials, stream, clientHeaders, model);
applyConfiguredUserAgent(headers, activeCredentials?.providerSpecificData);

// Strip OpenAI SDK (X-Stainless-*) metadata + normalize SDK-derived User-Agent
// on OpenAI-compatible passthrough requests — some upstream gateways 403 on them.
const strippedStainless = stripStainlessHeadersForOpenAICompat(headers, this.provider, url);
if (strippedStainless.length > 0) {
log?.debug?.(
"HEADERS",
`Stripped X-Stainless-* from OpenAI-compatible request: ${strippedStainless.join(", ")}`
);
}

const ccRequestDefaults = isClaudeCodeCompatible(this.provider)
? getClaudeCodeCompatibleRequestDefaults(activeCredentials?.providerSpecificData)
: {};
Expand Down
83 changes: 83 additions & 0 deletions tests/unit/executor-strip-stainless-openai-compat.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import test from "node:test";
import assert from "node:assert/strict";

const base = await import("../../open-sse/executors/base.ts");

test("isOpenAICompatibleEndpoint matches openai-compatible-* providers", () => {
assert.equal(base.isOpenAICompatibleEndpoint("openai-compatible-foo", "https://x/y"), true);
assert.equal(
base.isOpenAICompatibleEndpoint("claude", "https://api.anthropic.com/v1/messages"),
false
);
});

test("isOpenAICompatibleEndpoint matches chat/completions and responses URLs", () => {
assert.equal(
base.isOpenAICompatibleEndpoint("groq", "https://api.groq.com/openai/v1/chat/completions"),
true
);
assert.equal(base.isOpenAICompatibleEndpoint("groq", "https://x/v1/responses"), true);
assert.equal(base.isOpenAICompatibleEndpoint("groq", "https://x/v1/embeddings"), false);
});

test("strips X-Stainless-* headers on OpenAI-compatible passthrough", () => {
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: "Bearer sk-test",
"X-Stainless-Lang": "js",
"X-Stainless-OS": "Linux",
"x-stainless-runtime": "node",
};
const stripped = base.stripStainlessHeadersForOpenAICompat(
headers,
"openai-compatible-acme",
"https://acme.example/v1/chat/completions"
);
assert.deepEqual(stripped.sort(), ["X-Stainless-Lang", "X-Stainless-OS", "x-stainless-runtime"]);
assert.equal(headers["X-Stainless-Lang"], undefined);
assert.equal(headers["X-Stainless-OS"], undefined);
assert.equal(headers["x-stainless-runtime"], undefined);
// Non-stainless headers untouched.
assert.equal(headers["Authorization"], "Bearer sk-test");
assert.equal(headers["Content-Type"], "application/json");
});

test("does NOT strip X-Stainless-* for non-OpenAI-compatible endpoints", () => {
const headers: Record<string, string> = {
"X-Stainless-Lang": "js",
"X-Stainless-Package-Version": "1.0.0",
};
const stripped = base.stripStainlessHeadersForOpenAICompat(
headers,
"claude",
"https://api.anthropic.com/v1/messages"
);
assert.deepEqual(stripped, []);
// Claude-code-compat spoofing path keeps its X-Stainless-* headers intact.
assert.equal(headers["X-Stainless-Lang"], "js");
assert.equal(headers["X-Stainless-Package-Version"], "1.0.0");
});

test("normalizes SDK-derived User-Agent on OpenAI-compatible request", () => {
const headers: Record<string, string> = {
"User-Agent": "OpenAI/NodeJS 4.20.0 undici",
};
base.stripStainlessHeadersForOpenAICompat(
headers,
"openai-compatible-acme",
"https://acme.example/v1/chat/completions"
);
assert.equal(headers["User-Agent"], "Mozilla/5.0 (compatible; OpenAI Compatible)");
});

test("leaves a non-SDK User-Agent untouched", () => {
const headers: Record<string, string> = {
"User-Agent": "my-custom-agent/2.0",
};
base.stripStainlessHeadersForOpenAICompat(
headers,
"openai-compatible-acme",
"https://acme.example/v1/chat/completions"
);
assert.equal(headers["User-Agent"], "my-custom-agent/2.0");
});
Loading