feat: add CC Compatible connection-level 1M context toggle
Closes #1357
This commit is contained in:
+17
-21
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user