mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 05:45:04 +03:00
fix(executors): anthropic-compatible-* gateways get Bearer alongside x-api-key (#4729)
Integrated into release/v3.8.37 — cherry-picked defining commit onto release tip; tests green.
This commit is contained in:
committed by
GitHub
parent
b72a4d2fbb
commit
ab7c181b77
@@ -469,6 +469,21 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
} else if (credentials.accessToken) {
|
||||
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
||||
}
|
||||
// Port of decolua/9router commit b977bf74:
|
||||
// Third-party Anthropic-compatible gateways frequently require
|
||||
// Authorization: Bearer ALONGSIDE x-api-key — without it they
|
||||
// return 401 missing_api_key on every forward. Only emit the
|
||||
// Bearer fallback for non-official upstreams; api.anthropic.com
|
||||
// (and the empty/default baseUrl that targets it) must keep the
|
||||
// x-api-key-only behavior to avoid regressing the official path.
|
||||
if (effectiveKey && !headers["Authorization"]) {
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || "";
|
||||
const isOfficialAnthropic =
|
||||
baseUrl === "" || baseUrl.includes("api.anthropic.com");
|
||||
if (!isOfficialAnthropic) {
|
||||
headers["Authorization"] = `Bearer ${effectiveKey}`;
|
||||
}
|
||||
}
|
||||
if (!headers["anthropic-version"]) {
|
||||
headers["anthropic-version"] = "2023-06-01";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Port of decolua/9router commit b977bf74:
|
||||
* Some third-party Anthropic-compatible gateways (configured via
|
||||
* `anthropic-compatible-*` provider IDs) require Authorization: Bearer
|
||||
* in addition to x-api-key. Without the Bearer header, those gateways
|
||||
* return 401 "missing_api_key" on every forward.
|
||||
*
|
||||
* For NON-official anthropic-compatible endpoints (any `baseUrl` that is
|
||||
* not empty AND does not contain "api.anthropic.com"), the default
|
||||
* executor's buildHeaders must emit BOTH `x-api-key` and `Authorization:
|
||||
* Bearer <apiKey>`. Official api.anthropic.com upstreams are unchanged
|
||||
* (x-api-key only).
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { DefaultExecutor } from "../../open-sse/executors/default.ts";
|
||||
|
||||
const BASE_CREDS_THIRD_PARTY = {
|
||||
apiKey: "k-third-party",
|
||||
providerSpecificData: { baseUrl: "https://gateway.example/v1" },
|
||||
} as Record<string, unknown>;
|
||||
|
||||
const BASE_CREDS_OFFICIAL = {
|
||||
apiKey: "k-official",
|
||||
providerSpecificData: { baseUrl: "https://api.anthropic.com/v1" },
|
||||
} as Record<string, unknown>;
|
||||
|
||||
test("anthropic-compatible (third-party gateway): sends x-api-key AND Authorization: Bearer", () => {
|
||||
const executor = new DefaultExecutor("anthropic-compatible-thirdparty");
|
||||
const headers = executor.buildHeaders(BASE_CREDS_THIRD_PARTY, true) as Record<
|
||||
string,
|
||||
string
|
||||
>;
|
||||
assert.equal(headers["x-api-key"], "k-third-party");
|
||||
assert.equal(
|
||||
headers["Authorization"],
|
||||
"Bearer k-third-party",
|
||||
"third-party anthropic-compatible upstreams need the Bearer fallback too"
|
||||
);
|
||||
});
|
||||
|
||||
test("anthropic-compatible (official api.anthropic.com): only x-api-key, no Bearer", () => {
|
||||
const executor = new DefaultExecutor("anthropic-compatible-official");
|
||||
const headers = executor.buildHeaders(BASE_CREDS_OFFICIAL, true) as Record<
|
||||
string,
|
||||
string
|
||||
>;
|
||||
assert.equal(headers["x-api-key"], "k-official");
|
||||
assert.equal(
|
||||
headers["Authorization"],
|
||||
undefined,
|
||||
"official anthropic upstream must NOT receive a Bearer header alongside x-api-key"
|
||||
);
|
||||
});
|
||||
|
||||
test("anthropic-compatible (no baseUrl): treated as official, no Bearer", () => {
|
||||
// Empty/missing baseUrl means: "talk to api.anthropic.com" — keep the legacy
|
||||
// behavior (x-api-key only).
|
||||
const executor = new DefaultExecutor("anthropic-compatible-empty");
|
||||
const headers = executor.buildHeaders(
|
||||
{ apiKey: "k-empty", providerSpecificData: {} } as Record<string, unknown>,
|
||||
true
|
||||
) as Record<string, string>;
|
||||
assert.equal(headers["x-api-key"], "k-empty");
|
||||
assert.equal(headers["Authorization"], undefined);
|
||||
});
|
||||
Reference in New Issue
Block a user