Enable Codex goals in provider templates (#3089)

* Enable Codex goals in provider templates

* feat: add Codex goal mode toggle

- Remove forced goals=true from Codex provider presets and custom templates.
- Add a Codex provider editor switch that updates [features].goals on demand.
- Update docs, i18n, and regression coverage for the optional Goal mode flow.

---------

Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
Kairos Duan
2026-05-27 11:31:31 +08:00
committed by GitHub
parent e9d84af500
commit 3c3d417457
13 changed files with 325 additions and 25 deletions
@@ -1,10 +1,19 @@
// NOTE: Codex 1M 上下文 UI 已暂时隐藏(详见下方 CodexConfigSection 内 JSX 注释)。
// 如需恢复,请同时:
// - 在下方 React import 中加回 `useMemo`
// - 取消下面 `@/utils/providerConfigUtils` import 的注释
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useTranslation } from "react-i18next";
import JsonEditor from "@/components/JsonEditor";
import {
isCodexGoalModeEnabled,
setCodexGoalMode,
} from "@/utils/providerConfigUtils";
/*
import {
extractCodexTopLevelInt,
@@ -144,6 +153,18 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
[onChange],
);
const goalModeEnabled = useMemo(
() => isCodexGoalModeEnabled(localValue),
[localValue],
);
const handleGoalModeToggle = useCallback(
(checked: boolean) => {
handleLocalChange(setCodexGoalMode(localValueRef.current || "", checked));
},
[handleLocalChange],
);
// Codex 1M 上下文相关状态/回调暂时禁用——见同文件下方 JSX 注释处的恢复说明。
/*
// Parse toggle states from TOML text
@@ -217,7 +238,7 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<div className="flex flex-wrap items-center justify-between gap-2">
<label
htmlFor="codexConfig"
className="block text-sm font-medium text-foreground"
@@ -225,15 +246,27 @@ export const CodexConfigSection: React.FC<CodexConfigSectionProps> = ({
{t("codexConfig.configToml")}
</label>
<label className="inline-flex items-center gap-2 text-sm text-muted-foreground cursor-pointer">
<input
type="checkbox"
checked={useCommonConfig}
onChange={(e) => onCommonConfigToggle(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.writeCommonConfig")}
</label>
<div className="flex flex-wrap items-center justify-end gap-x-4 gap-y-1">
<label className="inline-flex cursor-pointer items-center gap-2 text-sm text-muted-foreground">
<input
type="checkbox"
checked={goalModeEnabled}
onChange={(e) => handleGoalModeToggle(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.enableGoalMode")}
</label>
<label className="inline-flex cursor-pointer items-center gap-2 text-sm text-muted-foreground">
<input
type="checkbox"
checked={useCommonConfig}
onChange={(e) => onCommonConfigToggle(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.writeCommonConfig")}
</label>
</div>
</div>
<div className="flex items-center justify-end">
+1
View File
@@ -1102,6 +1102,7 @@
"editCommonConfig": "Edit Common Config",
"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",
"apiUrlLabel": "API Request URL",
"extractFromCurrent": "Extract from Editor",
"extractNoCommonConfig": "No common config available to extract from editor",
+1
View File
@@ -1102,6 +1102,7 @@
"editCommonConfig": "共通設定を編集",
"editCommonConfigTitle": "Codex 共通設定スニペットを編集",
"commonConfigHint": "「共通設定を書き込む」がオンの場合、config.toml の末尾に追記されます",
"enableGoalMode": "Goal mode を有効化",
"apiUrlLabel": "API リクエスト URL",
"extractFromCurrent": "編集内容から抽出",
"extractNoCommonConfig": "編集内容から抽出できる共通設定がありません",
+1
View File
@@ -1074,6 +1074,7 @@
"editCommonConfig": "編輯通用設定",
"editCommonConfigTitle": "編輯 Codex 通用設定片段",
"commonConfigHint": "該片段會在勾選「寫入通用設定」時附加到 config.toml 末尾",
"enableGoalMode": "啟用 Goal mode",
"apiUrlLabel": "API 請求位址",
"extractFromCurrent": "從編輯內容擷取",
"extractNoCommonConfig": "目前編輯內容沒有可擷取的通用設定",
+1
View File
@@ -1102,6 +1102,7 @@
"editCommonConfig": "编辑通用配置",
"editCommonConfigTitle": "编辑 Codex 通用配置片段",
"commonConfigHint": "该片段会在勾选'写入通用配置'时追加到 config.toml 末尾",
"enableGoalMode": "启用 Goal mode",
"apiUrlLabel": "API 请求地址",
"extractFromCurrent": "从编辑内容提取",
"extractNoCommonConfig": "当前编辑内容没有可提取的通用配置",
+111
View File
@@ -410,6 +410,9 @@ 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_GOALS_FEATURE_PATTERN = /^\s*goals\s*=\s*(true|false)\s*(?:#.*)?$/;
const TOML_GOALS_FEATURE_REPLACE_PATTERN =
/^(\s*goals\s*=\s*)(true|false)(\s*(?:#.*)?)$/;
const CODEX_RESERVED_MODEL_PROVIDER_IDS = new Set([
"amazon-bedrock",
"openai",
@@ -422,6 +425,7 @@ const CODEX_RESERVED_MODEL_PROVIDER_IDS = new Set([
interface TomlSectionRange {
bodyEndIndex: number;
bodyStartIndex: number;
headerLineIndex: number;
}
interface TomlAssignmentMatch {
@@ -458,6 +462,7 @@ const getTomlSectionRange = (
return {
bodyStartIndex: headerLineIndex + 1,
bodyEndIndex: index,
headerLineIndex,
};
}
@@ -468,6 +473,7 @@ const getTomlSectionRange = (
return {
bodyStartIndex: headerLineIndex + 1,
bodyEndIndex: lines.length,
headerLineIndex,
};
};
@@ -639,6 +645,14 @@ const getTopLevelModelProviderLineIndex = (lines: string[]): number => {
return -1;
};
const hasTomlSectionBodyContent = (
lines: string[],
sectionRange: TomlSectionRange,
): boolean =>
lines
.slice(sectionRange.bodyStartIndex, sectionRange.bodyEndIndex)
.some((line) => line.trim() !== "");
const TOML_BASIC_STRING_ESCAPES: Record<string, string> = {
'"': '\\"',
"\\": "\\\\",
@@ -797,6 +811,103 @@ export const setCodexWireApi = (
return finalizeTomlText(lines);
};
export const isCodexGoalModeEnabled = (
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>;
return parsed.features?.goals === true;
} catch {
// Fall back to line scanning while the user is editing invalid TOML.
}
const lines = text.split("\n");
const featureRange = getTomlSectionRange(lines, "features");
if (!featureRange) return false;
const index = findTomlLineInRange(
lines,
TOML_GOALS_FEATURE_PATTERN,
featureRange.bodyStartIndex,
featureRange.bodyEndIndex,
);
if (index === -1) return false;
return lines[index].match(TOML_GOALS_FEATURE_PATTERN)?.[1] === "true";
} catch {
return false;
}
};
export const setCodexGoalMode = (
configText: string,
enabled: boolean,
): string => {
const normalizedText = normalizeTomlText(configText);
const lines = normalizedText ? normalizedText.split("\n") : [];
let featureRange = getTomlSectionRange(lines, "features");
if (featureRange) {
const goalLineIndex = findTomlLineInRange(
lines,
TOML_GOALS_FEATURE_REPLACE_PATTERN,
featureRange.bodyStartIndex,
featureRange.bodyEndIndex,
);
if (enabled) {
if (goalLineIndex !== -1) {
lines[goalLineIndex] = lines[goalLineIndex].replace(
TOML_GOALS_FEATURE_REPLACE_PATTERN,
"$1true$3",
);
} else {
lines.splice(
getTomlSectionInsertIndex(lines, featureRange),
0,
"goals = true",
);
}
return finalizeTomlText(lines);
}
if (goalLineIndex !== -1) {
lines.splice(goalLineIndex, 1);
featureRange = getTomlSectionRange(lines, "features");
if (featureRange && !hasTomlSectionBodyContent(lines, featureRange)) {
lines.splice(
featureRange.headerLineIndex,
featureRange.bodyEndIndex - featureRange.headerLineIndex,
);
}
}
return finalizeTomlText(lines);
}
if (!enabled) return normalizedText;
const topLevelEndIndex = getTopLevelEndIndex(lines);
const sectionLines: string[] = [];
if (topLevelEndIndex > 0 && lines[topLevelEndIndex - 1].trim() !== "") {
sectionLines.push("");
}
sectionLines.push("[features]", "goals = true");
if (
topLevelEndIndex < lines.length &&
lines[topLevelEndIndex]?.trim() !== ""
) {
sectionLines.push("");
}
lines.splice(topLevelEndIndex, 0, ...sectionLines);
return finalizeTomlText(lines);
};
// 从 Codex 的 TOML 配置文本中提取 base_url(支持单/双引号)
export const extractCodexBaseUrl = (
configText: string | undefined | null,