feat(codex): add remote compaction toggle for third-party providers

Write model_providers name as "OpenAI" to let Codex attempt remote
compaction through compatible endpoints. Hidden for official providers.
This commit is contained in:
Jason
2026-05-28 11:57:54 +08:00
parent a2ac21d0a6
commit af60c7ed2c
9 changed files with 215 additions and 0 deletions
@@ -7,6 +7,10 @@ interface CodexConfigEditorProps {
configValue: string;
providerName?: string;
showRemoteCompaction?: boolean;
onAuthChange: (value: string) => void;
onConfigChange: (value: string) => void;
@@ -37,6 +41,8 @@ interface CodexConfigEditorProps {
const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
authValue,
configValue,
providerName,
showRemoteCompaction,
onAuthChange,
onConfigChange,
onAuthBlur,
@@ -72,6 +78,8 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
<CodexConfigSection
value={configValue}
onChange={onConfigChange}
providerName={providerName}
showRemoteCompaction={showRemoteCompaction}
useCommonConfig={useCommonConfig}
onCommonConfigToggle={onCommonConfigToggle}
onEditCommonConfig={() => setIsCommonConfigModalOpen(true)}
@@ -12,7 +12,9 @@ import { useTranslation } from "react-i18next";
import JsonEditor from "@/components/JsonEditor";
import {
isCodexGoalModeEnabled,
isCodexRemoteCompactionEnabled,
setCodexGoalMode,
setCodexRemoteCompaction,
} from "@/utils/providerConfigUtils";
/*
import {
@@ -98,6 +100,8 @@ export const CodexAuthSection: React.FC<CodexAuthSectionProps> = ({
interface CodexConfigSectionProps {
value: string;
onChange: (value: string) => void;
providerName?: string;
showRemoteCompaction?: boolean;
useCommonConfig: boolean;
onCommonConfigToggle: (checked: boolean) => void;
onEditCommonConfig: () => void;
@@ -111,6 +115,8 @@ interface CodexConfigSectionProps {
export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
value,
onChange,
providerName,
showRemoteCompaction = true,
useCommonConfig,
onCommonConfigToggle,
onEditCommonConfig,
@@ -157,6 +163,10 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
() => isCodexGoalModeEnabled(localValue),
[localValue],
);
const remoteCompactionEnabled = useMemo(
() => isCodexRemoteCompactionEnabled(localValue),
[localValue],
);
const handleGoalModeToggle = useCallback(
(checked: boolean) => {
@@ -165,6 +175,19 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
[handleLocalChange],
);
const handleRemoteCompactionToggle = useCallback(
(checked: boolean) => {
handleLocalChange(
setCodexRemoteCompaction(
localValueRef.current || "",
checked,
providerName,
),
);
},
[handleLocalChange, providerName],
);
// Codex 1M 上下文相关状态/回调暂时禁用——见同文件下方 JSX 注释处的恢复说明。
/*
// Parse toggle states from TOML text
@@ -257,6 +280,21 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
{t("codexConfig.enableGoalMode")}
</label>
{showRemoteCompaction && (
<label
className="inline-flex cursor-pointer items-center gap-2 text-sm text-muted-foreground"
title={t("codexConfig.remoteCompactionHint")}
>
<input
type="checkbox"
checked={remoteCompactionEnabled}
onChange={(e) => handleRemoteCompactionToggle(e.target.checked)}
className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-border-default rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2"
/>
{t("codexConfig.enableRemoteCompaction")}
</label>
)}
<label className="inline-flex cursor-pointer items-center gap-2 text-sm text-muted-foreground">
<input
type="checkbox"
@@ -2163,6 +2163,8 @@ function ProviderFormFull({
<CodexConfigEditor
authValue={codexAuth}
configValue={codexConfig}
providerName={form.watch("name")}
showRemoteCompaction={category !== "official"}
onAuthChange={setCodexAuth}
onConfigChange={handleCodexConfigChange}
useCommonConfig={useCodexCommonConfigFlag}
+2
View File
@@ -1103,6 +1103,8 @@
"editCommonConfigTitle": "Edit Codex Common Config Snippet",
"commonConfigHint": "This snippet will be appended to the end of config.toml when 'Write Common Config' is checked",
"enableGoalMode": "Enable Goal mode",
"enableRemoteCompaction": "Enable remote compaction",
"remoteCompactionHint": "When enabled, the active model_providers entry name is written as OpenAI so Codex can try remote compaction.",
"apiUrlLabel": "API Request URL",
"extractFromCurrent": "Extract from Editor",
"extractNoCommonConfig": "No common config available to extract from editor",
+2
View File
@@ -1103,6 +1103,8 @@
"editCommonConfigTitle": "Codex 共通設定スニペットを編集",
"commonConfigHint": "「共通設定を書き込む」がオンの場合、config.toml の末尾に追記されます",
"enableGoalMode": "Goal mode を有効化",
"enableRemoteCompaction": "リモート圧縮を有効化",
"remoteCompactionHint": "有効にすると、現在の model_providers エントリの name を OpenAI に設定し、Codex がリモート圧縮を試行できるようにします。",
"apiUrlLabel": "API リクエスト URL",
"extractFromCurrent": "編集内容から抽出",
"extractNoCommonConfig": "編集内容から抽出できる共通設定がありません",
+2
View File
@@ -1075,6 +1075,8 @@
"editCommonConfigTitle": "編輯 Codex 通用設定片段",
"commonConfigHint": "該片段會在勾選「寫入通用設定」時附加到 config.toml 末尾",
"enableGoalMode": "啟用 Goal mode",
"enableRemoteCompaction": "啟用遠端壓縮",
"remoteCompactionHint": "開啟後會將目前 model_providers 條目的 name 寫為 OpenAI,讓 Codex 嘗試使用遠端壓縮。",
"apiUrlLabel": "API 請求位址",
"extractFromCurrent": "從編輯內容擷取",
"extractNoCommonConfig": "目前編輯內容沒有可擷取的通用設定",
+2
View File
@@ -1103,6 +1103,8 @@
"editCommonConfigTitle": "编辑 Codex 通用配置片段",
"commonConfigHint": "该片段会在勾选'写入通用配置'时追加到 config.toml 末尾",
"enableGoalMode": "启用 Goal mode",
"enableRemoteCompaction": "启用远程压缩",
"remoteCompactionHint": "开启后会将当前 model_providers 条目的 name 写为 OpenAI,使 Codex 尝试使用远程压缩。",
"apiUrlLabel": "API 请求地址",
"extractFromCurrent": "从编辑内容提取",
"extractNoCommonConfig": "当前编辑内容没有可提取的通用配置",
+52
View File
@@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import {
isCodexRemoteCompactionEnabled,
setCodexRemoteCompaction,
} from "./providerConfigUtils";
describe("Codex remote compaction config helpers", () => {
it("enables remote compaction by naming the active custom provider OpenAI", () => {
const input = `model_provider = "custom"
model = "gpt-5.4"
[model_providers.custom]
name = "AIHubMix"
base_url = "https://aihubmix.example/v1"
wire_api = "responses"
[model_providers.backup]
name = "Backup"
base_url = "https://backup.example/v1"
`;
const result = setCodexRemoteCompaction(input, true, "AIHubMix");
expect(isCodexRemoteCompactionEnabled(result)).toBe(true);
expect(result).toContain(`[model_providers.custom]\nname = "OpenAI"`);
expect(result).toContain(`[model_providers.backup]\nname = "Backup"`);
});
it("disables remote compaction by restoring the provider display name", () => {
const input = `model_provider = "custom"
[model_providers.custom]
name = "OpenAI"
base_url = "https://aihubmix.example/v1"
wire_api = "responses"
`;
const result = setCodexRemoteCompaction(input, false, "AIHubMix");
expect(isCodexRemoteCompactionEnabled(result)).toBe(false);
expect(result).toContain(`name = "AIHubMix"`);
});
it("does not rewrite reserved built-in providers", () => {
const input = `model_provider = "openai"
model = "gpt-5"
`;
expect(setCodexRemoteCompaction(input, true, "OpenAI")).toBe(input);
expect(isCodexRemoteCompactionEnabled(input)).toBe(false);
});
});
+107
View File
@@ -410,6 +410,10 @@ const TOML_WIRE_API_PATTERN =
/^\s*wire_api\s*=\s*(["'])([^"'\r\n]+)\1\s*(?:#.*)?$/;
const TOML_MODEL_PROVIDER_LINE_PATTERN =
/^\s*model_provider\s*=\s*(["'])([^"'\r\n]+)\1\s*(?:#.*)?$/;
const TOML_PROVIDER_NAME_PATTERN =
/^\s*name\s*=\s*(["'])([^"'\r\n]+)\1\s*(?:#.*)?$/;
const TOML_PROVIDER_NAME_REPLACE_PATTERN =
/^(\s*name\s*=\s*)(?:"(?:\\.|[^"\\\r\n])*"|'[^'\r\n]*')(\s*(?:#.*)?)$/;
const TOML_GOALS_FEATURE_PATTERN = /^\s*goals\s*=\s*(true|false)\s*(?:#.*)?$/;
const TOML_GOALS_FEATURE_REPLACE_PATTERN =
/^(\s*goals\s*=\s*)(true|false)(\s*(?:#.*)?)$/;
@@ -670,6 +674,9 @@ const escapeTomlBasicString = (value: string): string =>
return `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`;
});
const tomlBasicString = (value: string): string =>
`"${escapeTomlBasicString(value)}"`;
const CODEX_CHAT_WIRE_API_VALUES = new Set([
"chat",
"chat_completions",
@@ -908,6 +915,106 @@ export const setCodexGoalMode = (
return finalizeTomlText(lines);
};
export const isCodexRemoteCompactionEnabled = (
configText: string | undefined | null,
): boolean => {
try {
const raw = typeof configText === "string" ? configText : "";
const text = normalizeTomlText(raw);
if (!text) return false;
try {
const parsed = parseToml(text) as Record<string, any>;
const providerId =
typeof parsed.model_provider === "string"
? parsed.model_provider.trim()
: "";
if (!providerId || !isCustomCodexModelProviderId(providerId)) {
return false;
}
return parsed.model_providers?.[providerId]?.name === "OpenAI";
} catch {
// Fall back to line scanning while the user is editing invalid TOML.
}
const lines = text.split("\n");
const targetSectionName = getCodexCustomProviderSectionName(text);
if (!targetSectionName) return false;
const sectionRange = getTomlSectionRange(lines, targetSectionName);
if (!sectionRange) return false;
const nameAssignment = findTomlAssignmentInRange(
lines,
TOML_PROVIDER_NAME_PATTERN,
sectionRange.bodyStartIndex,
sectionRange.bodyEndIndex,
targetSectionName,
);
return nameAssignment?.value === "OpenAI";
} catch {
return false;
}
};
export const setCodexRemoteCompaction = (
configText: string,
enabled: boolean,
fallbackProviderName?: string,
): string => {
const normalizedText = normalizeTomlText(configText);
const lines = normalizedText ? normalizedText.split("\n") : [];
const targetSectionName = getCodexCustomProviderSectionName(normalizedText);
if (!targetSectionName) {
return normalizedText;
}
let targetSectionRange = getTomlSectionRange(lines, targetSectionName);
const replacementName = enabled
? "OpenAI"
: fallbackProviderName?.trim() ||
getCodexModelProviderName(normalizedText) ||
"custom";
const replacementLine = `name = ${tomlBasicString(replacementName)}`;
if (targetSectionRange) {
const nameLine = findTomlLineInRange(
lines,
TOML_PROVIDER_NAME_REPLACE_PATTERN,
targetSectionRange.bodyStartIndex,
targetSectionRange.bodyEndIndex,
);
if (nameLine !== -1) {
lines[nameLine] = lines[nameLine].replace(
TOML_PROVIDER_NAME_REPLACE_PATTERN,
`$1${tomlBasicString(replacementName)}$2`,
);
return finalizeTomlText(lines);
}
lines.splice(
getTomlSectionInsertIndex(lines, targetSectionRange),
0,
replacementLine,
);
return finalizeTomlText(lines);
}
if (!enabled) return normalizedText;
if (lines.length > 0 && lines[lines.length - 1].trim() !== "") {
lines.push("");
}
lines.push(`[${targetSectionName}]`, replacementLine);
targetSectionRange = getTomlSectionRange(lines, targetSectionName);
return targetSectionRange ? finalizeTomlText(lines) : normalizedText;
};
// 从 Codex 的 TOML 配置文本中提取 base_url(支持单/双引号)
export const extractCodexBaseUrl = (
configText: string | undefined | null,