feat: add CC Compatible connection-level 1M context toggle

Closes #1357
This commit is contained in:
diegosouzapw
2026-04-17 16:59:18 -03:00
parent 792a1cb2ab
commit 1e3ac5fff7
8 changed files with 336 additions and 24 deletions
+17 -21
View File
@@ -4,6 +4,12 @@ import { getRotatingApiKey } from "../services/apiKeyRotator.ts";
import { getOpenAICompatibleType, isClaudeCodeCompatible } from "../services/provider.ts";
import type { ProviderRequestDefaults } from "../services/providerRequestDefaults.ts";
import { signRequestBody } from "../services/claudeCodeCCH.ts";
import {
appendAnthropicBetaHeader,
CONTEXT_1M_BETA_HEADER,
modelSupportsContext1mBeta,
} from "../services/claudeCodeCompatible.ts";
import { getClaudeCodeCompatibleRequestDefaults } from "@/lib/providers/requestDefaults";
/**
* Sanitizes a custom API path to prevent path traversal attacks.
@@ -386,27 +392,17 @@ export class BaseExecutor {
const headers = this.buildHeaders(activeCredentials, stream);
applyConfiguredUserAgent(headers, activeCredentials?.providerSpecificData);
// Append 1M context beta header when [1m] suffix was used
// Only supported for specific Claude models per Anthropic docs
if (extendedContext) {
const EXTENDED_CONTEXT_MODELS = [
"claude-opus-4-7",
"claude-opus-4-6",
"claude-sonnet-4-6",
"claude-sonnet-4-5",
"claude-sonnet-4",
];
const baseModel = model.replace(/-\d{8}$/, "");
if (
EXTENDED_CONTEXT_MODELS.some((m) => baseModel === m || model === m || model.startsWith(m))
) {
const existing = headers["Anthropic-Beta"];
if (existing) {
headers["Anthropic-Beta"] = existing + ",context-1m-2025-08-07";
} else {
headers["Anthropic-Beta"] = "context-1m-2025-08-07";
}
}
const ccRequestDefaults = isClaudeCodeCompatible(this.provider)
? getClaudeCodeCompatibleRequestDefaults(activeCredentials?.providerSpecificData)
: {};
const shouldForwardExtendedContext =
extendedContext &&
modelSupportsContext1mBeta(model) &&
!isClaudeCodeCompatible(this.provider);
const shouldForwardCcCompatibleContext1m =
isClaudeCodeCompatible(this.provider) && ccRequestDefaults.context1m === true;
if (shouldForwardExtendedContext || shouldForwardCcCompatibleContext1m) {
appendAnthropicBetaHeader(headers, CONTEXT_1M_BETA_HEADER);
}
const transformedBody = await this.transformRequest(model, body, stream, activeCredentials);
+53
View File
@@ -35,10 +35,32 @@ export const CLAUDE_CODE_COMPATIBLE_PREFIX = "anthropic-compatible-cc-";
export const CLAUDE_CODE_COMPATIBLE_DEFAULT_CHAT_PATH = "/v1/messages?beta=true";
export const CLAUDE_CODE_COMPATIBLE_DEFAULT_MODELS_PATH = "/models";
export const CLAUDE_CODE_COMPATIBLE_DEFAULT_MAX_TOKENS = 8092;
<<<<<<< HEAD
export const CLAUDE_CODE_COMPATIBLE_ANTHROPIC_VERSION = ANTHROPIC_VERSION_HEADER;
export const CLAUDE_CODE_COMPATIBLE_ANTHROPIC_BETA = ANTHROPIC_BETA_FULL;
export const CLAUDE_CODE_COMPATIBLE_VERSION = CLAUDE_CLI_VERSION;
export const CLAUDE_CODE_COMPATIBLE_USER_AGENT = CLAUDE_CLI_USER_AGENT;
||||||| d868124c
export const CLAUDE_CODE_COMPATIBLE_ANTHROPIC_VERSION = "2023-06-01";
export const CLAUDE_CODE_COMPATIBLE_ANTHROPIC_BETA =
"claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,effort-2025-11-24,token-efficient-tools-2025-02-19";
export const CLAUDE_CODE_COMPATIBLE_VERSION = "2.1.87";
export const CLAUDE_CODE_COMPATIBLE_USER_AGENT = `claude-cli/${CLAUDE_CODE_COMPATIBLE_VERSION} (external, cli)`;
=======
export const CLAUDE_CODE_COMPATIBLE_ANTHROPIC_VERSION = "2023-06-01";
export const CLAUDE_CODE_COMPATIBLE_ANTHROPIC_BETA =
"claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,effort-2025-11-24,token-efficient-tools-2025-02-19";
export const CONTEXT_1M_BETA_HEADER = "context-1m-2025-08-07";
export const CLAUDE_CODE_COMPATIBLE_VERSION = "2.1.87";
export const CLAUDE_CODE_COMPATIBLE_USER_AGENT = `claude-cli/${CLAUDE_CODE_COMPATIBLE_VERSION} (external, cli)`;
const CONTEXT_1M_SUPPORTED_MODELS = [
"claude-opus-4-7",
"claude-opus-4-6",
"claude-sonnet-4-6",
"claude-sonnet-4-5",
"claude-sonnet-4",
];
>>>>>>> coder/cc-compatible-1m-toggle
/**
* Build the billing header dynamically with fingerprint and CCH placeholder.
* The cch=00000 placeholder is later replaced by signRequestBody().
@@ -128,6 +150,37 @@ export function joinClaudeCodeCompatibleUrl(baseUrl: string, path: string): stri
return joinNormalizedBaseUrlAndPath(stripClaudeCodeCompatibleEndpointSuffix(baseUrl), path);
}
export function appendAnthropicBetaHeader(
headers: Record<string, string>,
betaHeader: string
): void {
const existingKey = Object.keys(headers).find((key) => key.toLowerCase() === "anthropic-beta");
if (!existingKey) {
headers["anthropic-beta"] = betaHeader;
return;
}
const existingValues = String(headers[existingKey] || "")
.split(",")
.map((value) => value.trim())
.filter(Boolean);
if (!existingValues.includes(betaHeader)) {
headers[existingKey] = [...existingValues, betaHeader].join(",");
}
}
export function modelSupportsContext1mBeta(model: string | null | undefined): boolean {
const normalizedModel = String(model || "")
.trim()
.toLowerCase()
.replace(/-\d{8}$/, "");
return CONTEXT_1M_SUPPORTED_MODELS.some(
(supported) => normalizedModel === supported || normalizedModel.startsWith(`${supported}-`)
);
}
export function buildClaudeCodeCompatibleHeaders(
apiKey: string,
stream = false,
@@ -48,7 +48,10 @@ import { resolveManagedModelAlias } from "@/shared/utils/providerModelAliases";
import { maskEmail, pickMaskedDisplayValue, pickDisplayValue } from "@/shared/utils/maskEmail";
import useEmailPrivacyStore from "@/store/emailPrivacyStore";
import EmailPrivacyToggle from "@/shared/components/EmailPrivacyToggle";
import { getCodexRequestDefaults as _getCodexRequestDefaults } from "@/lib/providers/requestDefaults";
import {
getClaudeCodeCompatibleRequestDefaults as _getClaudeCodeCompatibleRequestDefaults,
getCodexRequestDefaults as _getCodexRequestDefaults,
} from "@/lib/providers/requestDefaults";
import { resolveDashboardProviderInfo } from "../providerPageUtils";
type CompatByProtocolMap = Partial<
@@ -608,6 +611,15 @@ function getCodexRequestDefaults(providerSpecificData: unknown): {
};
}
function getClaudeCodeCompatibleRequestDefaults(providerSpecificData: unknown): {
context1m: boolean;
} {
const defaults = _getClaudeCodeCompatibleRequestDefaults(providerSpecificData);
return {
context1m: defaults.context1m === true,
};
}
function compatProtocolLabelKey(protocol: string): string {
if (protocol === "openai") return "compatProtocolOpenAI";
if (protocol === "openai-responses") return "compatProtocolOpenAIResponses";
@@ -5389,6 +5401,7 @@ function AddApiKeyModal({
customUserAgent: "",
accountId: "",
consoleApiKey: "",
ccCompatibleContext1m: false,
});
const [validating, setValidating] = useState(false);
const [validationResult, setValidationResult] = useState(null);
@@ -5497,6 +5510,9 @@ function AddApiKeyModal({
} else if (isCloudflare && formData.accountId.trim()) {
providerSpecificData.accountId = formData.accountId.trim();
}
if (isCcCompatible && formData.ccCompatibleContext1m) {
providerSpecificData.requestDefaults = { context1m: true };
}
const payload = {
name: formData.name,
@@ -5605,6 +5621,16 @@ function AddApiKeyModal({
{saveError}
</div>
)}
{isCcCompatible && (
<div className="flex flex-col gap-4 rounded-lg border border-border/50 bg-surface/20 p-4">
<Toggle
checked={formData.ccCompatibleContext1m}
onChange={(checked) => setFormData({ ...formData, ccCompatibleContext1m: checked })}
label="CC Compatible 1M Context"
description="When enabled, this connection appends `anthropic-beta: context-1m-2025-08-07`."
/>
</div>
)}
{isCompatible && (
<p className="text-xs text-text-muted">
{isCcCompatible
@@ -5797,6 +5823,7 @@ function EditConnectionModal({ isOpen, connection, onSave, onClose }: EditConnec
codexFastServiceTier: false,
codexOpenaiStoreEnabled: false,
consoleApiKey: "",
ccCompatibleContext1m: false,
});
const [testing, setTesting] = useState(false);
const [testResult, setTestResult] = useState(null);
@@ -5819,6 +5846,7 @@ function EditConnectionModal({ isOpen, connection, onSave, onClose }: EditConnec
const isSearxng = connection?.provider === "searxng-search";
const isGooglePse = connection?.provider === "google-pse-search";
const apiKeyOptional = isSearxng;
const isCcCompatible = isClaudeCodeCompatibleProvider(connection?.provider);
const defaultRegion = "us-central1";
useEffect(() => {
@@ -5835,6 +5863,9 @@ function EditConnectionModal({ isOpen, connection, onSave, onClose }: EditConnec
const rawAccountId = connection.providerSpecificData?.accountId;
const existingAccountId = typeof rawAccountId === "string" ? rawAccountId : "";
const codexRequestDefaults = getCodexRequestDefaults(connection.providerSpecificData);
const ccRequestDefaults = getClaudeCodeCompatibleRequestDefaults(
connection.providerSpecificData
);
const rawConsoleApiKey = connection.providerSpecificData?.consoleApiKey;
const existingConsoleApiKey = typeof rawConsoleApiKey === "string" ? rawConsoleApiKey : "";
setFormData({
@@ -5859,6 +5890,7 @@ function EditConnectionModal({ isOpen, connection, onSave, onClose }: EditConnec
codexFastServiceTier: codexRequestDefaults.serviceTier === "priority",
codexOpenaiStoreEnabled: connection.providerSpecificData?.openaiStoreEnabled === true,
consoleApiKey: existingConsoleApiKey,
ccCompatibleContext1m: ccRequestDefaults.context1m,
});
// Load existing extra keys from providerSpecificData
const existing = connection.providerSpecificData?.extraApiKeys;
@@ -6022,6 +6054,21 @@ function EditConnectionModal({ isOpen, connection, onSave, onClose }: EditConnec
} else if (isCloudflare && formData.accountId.trim()) {
updates.providerSpecificData.accountId = formData.accountId.trim();
}
if (isCcCompatible) {
const currentRequestDefaults =
updates.providerSpecificData.requestDefaults &&
typeof updates.providerSpecificData.requestDefaults === "object" &&
!Array.isArray(updates.providerSpecificData.requestDefaults)
? { ...(updates.providerSpecificData.requestDefaults as Record<string, unknown>) }
: {};
if (formData.ccCompatibleContext1m) {
currentRequestDefaults.context1m = true;
} else {
delete currentRequestDefaults.context1m;
}
updates.providerSpecificData.requestDefaults =
Object.keys(currentRequestDefaults).length > 0 ? currentRequestDefaults : undefined;
}
} else {
// Also persist tag for OAuth accounts
updates.providerSpecificData = {
@@ -6112,6 +6159,16 @@ function EditConnectionModal({ isOpen, connection, onSave, onClose }: EditConnec
/>
</div>
)}
{isCcCompatible && (
<div className="flex flex-col gap-4 rounded-lg border border-border/50 bg-surface/20 p-4">
<Toggle
checked={formData.ccCompatibleContext1m}
onChange={(checked) => setFormData({ ...formData, ccCompatibleContext1m: checked })}
label="CC Compatible 1M Context"
description="When enabled, this connection appends `anthropic-beta: context-1m-2025-08-07`."
/>
</div>
)}
{isOAuth && connection.email && (
<div className="bg-sidebar/50 p-3 rounded-lg">
<p className="text-sm text-text-muted mb-1">{t("email")}</p>
+33
View File
@@ -1,4 +1,5 @@
type JsonRecord = Record<string, unknown>;
const CLAUDE_CODE_COMPATIBLE_PROVIDER_PREFIX = "anthropic-compatible-cc-";
import { normalizeExcludedModelPatterns } from "@/domain/connectionModelRules";
import { normalizeRoutingTags } from "@/domain/tagRouter";
@@ -23,6 +24,12 @@ function hasNonEmptyString(value: unknown): boolean {
return typeof value === "string" && value.trim().length > 0;
}
function isClaudeCodeCompatibleProvider(provider: string | null | undefined): boolean {
return (
typeof provider === "string" && provider.startsWith(CLAUDE_CODE_COMPATIBLE_PROVIDER_PREFIX)
);
}
export function normalizeCodexReasoningEffort(value: unknown): CodexReasoningEffort | undefined {
const normalized = normalizeString(value);
if (!normalized || !CODEX_REASONING_EFFORT_SET.has(normalized)) {
@@ -38,6 +45,10 @@ export function normalizeCodexServiceTier(value: unknown): "priority" | undefine
return undefined;
}
export function normalizeClaudeCodeCompatibleContext1m(value: unknown): true | undefined {
return value === true ? true : undefined;
}
export function normalizeRequestDefaults(
provider: string | null | undefined,
value: unknown
@@ -63,6 +74,15 @@ export function normalizeRequestDefaults(
}
}
if (isClaudeCodeCompatibleProvider(provider)) {
const context1m = normalizeClaudeCodeCompatibleContext1m(record.context1m);
if (context1m) {
normalized.context1m = true;
} else {
delete normalized.context1m;
}
}
return Object.keys(normalized).length > 0 ? normalized : undefined;
}
@@ -188,3 +208,16 @@ export function getCodexRequestDefaults(providerSpecificData: unknown): {
...(serviceTier ? { serviceTier } : {}),
};
}
export function getClaudeCodeCompatibleRequestDefaults(providerSpecificData: unknown): {
context1m?: true;
} {
const defaults = getProviderRequestDefaults(
"anthropic-compatible-cc-default",
providerSpecificData
);
const context1m = normalizeClaudeCodeCompatibleContext1m(defaults.context1m);
return {
...(context1m ? { context1m } : {}),
};
}
+9
View File
@@ -99,6 +99,15 @@ function validateProviderSpecificData(
path: ["requestDefaults", "serviceTier"],
});
}
const context1m = requestDefaultsRecord.context1m;
if (context1m !== undefined && context1m !== null && typeof context1m !== "boolean") {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "providerSpecificData.requestDefaults.context1m must be a boolean",
path: ["requestDefaults", "context1m"],
});
}
}
}
+86
View File
@@ -14,6 +14,7 @@ import { PROVIDERS } from "../../open-sse/config/constants.ts";
import {
CLAUDE_CODE_COMPATIBLE_ANTHROPIC_VERSION,
CLAUDE_CODE_COMPATIBLE_DEFAULT_CHAT_PATH,
CONTEXT_1M_BETA_HEADER,
} from "../../open-sse/services/claudeCodeCompatible.ts";
class TestExecutor extends BaseExecutor {
@@ -322,6 +323,91 @@ test("DefaultExecutor.buildHeaders rotates extra API keys and builds Claude Code
assert.equal(ccJsonHeaders.Accept, "application/json");
});
test("DefaultExecutor.execute uses CC-compatible connection defaults to append 1M beta", async () => {
const originalFetch = globalThis.fetch;
const calls = [];
const toPlainHeaders = (headers) =>
headers instanceof Headers
? Object.fromEntries(headers.entries())
: Object.fromEntries(
Object.entries(headers || {}).map(([key, value]) => [
key,
value == null ? "" : String(value),
])
);
globalThis.fetch = async (_url, init = {}) => {
calls.push({ headers: toPlainHeaders(init.headers) });
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
try {
const cc = new DefaultExecutor("anthropic-compatible-cc-test");
await cc.execute({
model: "claude-sonnet-4-6",
body: {
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "hi" }],
max_tokens: 1,
},
stream: false,
credentials: {
apiKey: "cc-key",
providerSpecificData: {
baseUrl: "https://cc.example.com/v1/messages?beta=true",
ccSessionId: "session-1",
},
},
extendedContext: false,
});
await cc.execute({
model: "claude-sonnet-4-6",
body: {
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "hi" }],
max_tokens: 1,
},
stream: false,
credentials: {
apiKey: "cc-key",
providerSpecificData: {
baseUrl: "https://cc.example.com/v1/messages?beta=true",
ccSessionId: "session-1",
requestDefaults: { context1m: true },
},
},
extendedContext: false,
});
const anthropicCompat = new DefaultExecutor("anthropic-compatible-test");
await anthropicCompat.execute({
model: "claude-sonnet-4-6",
body: {
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "hi" }],
max_tokens: 1,
},
stream: false,
credentials: {
apiKey: "anth-key",
providerSpecificData: {
baseUrl: "https://anthropic.example.com/v1",
},
},
extendedContext: true,
});
} finally {
globalThis.fetch = originalFetch;
}
assert.equal(calls[0].headers["anthropic-beta"].includes(CONTEXT_1M_BETA_HEADER), false);
assert.equal(calls[1].headers["anthropic-beta"].includes(CONTEXT_1M_BETA_HEADER), true);
assert.equal(calls[2].headers["anthropic-beta"], CONTEXT_1M_BETA_HEADER);
});
test("DefaultExecutor.transformRequest is a passthrough and preserves model ids with slashes", () => {
const executor = new DefaultExecutor("openai");
const body = { model: "zai-org/GLM-5-FP8", messages: [{ role: "user", content: "hi" }] };
@@ -41,3 +41,49 @@ test("provider schemas reject non-boolean openaiStoreEnabled values", () => {
assert.equal(created.success, false);
assert.equal(updated.success, false);
});
test("provider schemas accept boolean requestDefaults.context1m for CC-compatible providers", () => {
const created = createProviderSchema.safeParse({
provider: "anthropic-compatible-cc-demo",
apiKey: "token",
name: "CC Compatible",
providerSpecificData: {
requestDefaults: {
context1m: true,
},
},
});
const updated = updateProviderConnectionSchema.safeParse({
providerSpecificData: {
requestDefaults: {
context1m: false,
},
},
});
assert.equal(created.success, true);
assert.equal(updated.success, true);
});
test("provider schemas reject non-boolean requestDefaults.context1m values", () => {
const created = createProviderSchema.safeParse({
provider: "anthropic-compatible-cc-demo",
apiKey: "token",
name: "CC Compatible",
providerSpecificData: {
requestDefaults: {
context1m: "yes",
},
},
});
const updated = updateProviderConnectionSchema.safeParse({
providerSpecificData: {
requestDefaults: {
context1m: 1,
},
},
});
assert.equal(created.success, false);
assert.equal(updated.success, false);
});
@@ -1,8 +1,12 @@
import test from "node:test";
import assert from "node:assert/strict";
const { buildOpenAIStoreSessionId, ensureOpenAIStoreSessionFallback } =
await import("../../src/lib/providers/requestDefaults.ts");
const {
buildOpenAIStoreSessionId,
ensureOpenAIStoreSessionFallback,
getClaudeCodeCompatibleRequestDefaults,
normalizeProviderSpecificData,
} = await import("../../src/lib/providers/requestDefaults.ts");
test("buildOpenAIStoreSessionId normalizes external and generated session ids", () => {
assert.equal(
@@ -38,3 +42,31 @@ test("ensureOpenAIStoreSessionFallback injects session_id only when no stable ca
);
assert.equal(withExplicitSession.session_id, "existing-session");
});
test("normalizeProviderSpecificData keeps only boolean CC-compatible 1M request defaults", () => {
const normalized = normalizeProviderSpecificData("anthropic-compatible-cc-demo", {
baseUrl: "https://proxy.example.com/v1/messages?beta=true",
requestDefaults: {
context1m: true,
customFlag: "keep-me",
},
});
assert.deepEqual(getClaudeCodeCompatibleRequestDefaults(normalized), {
context1m: true,
});
assert.deepEqual(normalized?.requestDefaults, {
context1m: true,
customFlag: "keep-me",
});
const stripped = normalizeProviderSpecificData("anthropic-compatible-cc-demo", {
requestDefaults: {
context1m: "yes",
customFlag: "keep-me",
},
});
assert.deepEqual(stripped?.requestDefaults, {
customFlag: "keep-me",
});
});