mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
feat(codex): decouple upstream format selector from model-mapping toggle
The Codex provider form tied Chat-format conversion and route takeover (model mapping) to one toggle, so a provider serving a native Responses API could not use model mapping without forcing Chat Completions conversion. - Promote the upstream format (Chat Completions / Responses) to an independent, always-visible selector that triggers no sub-menus on its own. - The local-routing toggle now solely gates the advanced sub-sections: model mapping catalog, plus reasoning capability when the format is Chat. - Persist modelCatalog / codexChatReasoning based on the takeover toggle and derive its initial state from saved catalog presence (no new persisted field). - Refresh codexConfig i18n (zh/en/ja/zh-TW): add upstreamFormat* keys and reword the routing/advanced hints to reflect the decoupling. - Fix codexChatProviderPresets test expectation for the Doubao Seed 2.1 Pro rename.
This commit is contained in:
@@ -150,6 +150,16 @@ const codexApiFormatFromWireApi = (
|
||||
}
|
||||
};
|
||||
|
||||
// 从已保存的 settingsConfig 推断 Codex 模型映射条目数(用于决定本地路由初始开关)。
|
||||
const codexCatalogCountFromSettings = (settingsConfig: unknown): number => {
|
||||
if (settingsConfig && typeof settingsConfig === "object") {
|
||||
const models = (settingsConfig as { modelCatalog?: { models?: unknown } })
|
||||
.modelCatalog?.models;
|
||||
return Array.isArray(models) ? models.length : 0;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const normalizeCodexCatalogModelsForSave = (
|
||||
models: CodexCatalogModel[],
|
||||
): CodexCatalogModel[] => {
|
||||
@@ -568,24 +578,29 @@ function ProviderFormFull({
|
||||
resetCodexConfig,
|
||||
} = useCodexConfigState({ initialData });
|
||||
|
||||
const initialCodexApiFormat: CodexApiFormat =
|
||||
initialData?.meta?.apiFormat === "openai_chat"
|
||||
? "openai_chat"
|
||||
: initialData?.meta?.apiFormat === "openai_responses"
|
||||
? "openai_responses"
|
||||
: (codexApiFormatFromWireApi(
|
||||
extractCodexWireApi(
|
||||
typeof initialData?.settingsConfig?.config === "string"
|
||||
? initialData.settingsConfig.config
|
||||
: "",
|
||||
),
|
||||
) ?? "openai_responses");
|
||||
|
||||
const [localCodexApiFormat, setLocalCodexApiFormat] =
|
||||
useState<CodexApiFormat>(() => {
|
||||
if (initialData?.meta?.apiFormat === "openai_chat") {
|
||||
return "openai_chat";
|
||||
}
|
||||
if (initialData?.meta?.apiFormat === "openai_responses") {
|
||||
return "openai_responses";
|
||||
}
|
||||
return (
|
||||
codexApiFormatFromWireApi(
|
||||
extractCodexWireApi(
|
||||
typeof initialData?.settingsConfig?.config === "string"
|
||||
? initialData.settingsConfig.config
|
||||
: "",
|
||||
),
|
||||
) ?? "openai_responses"
|
||||
);
|
||||
});
|
||||
useState<CodexApiFormat>(initialCodexApiFormat);
|
||||
|
||||
// 本地路由(接管)开关 —— 纯模型映射门控,与上游格式完全独立。
|
||||
// 没有独立持久化字段,初值仅按「是否已配置模型映射」推断(有 catalog 即视为
|
||||
// 接管已开)。只在 useState 初始化与预设重置点设置,跟 localCodexApiFormat
|
||||
// 对称,避免漂移。
|
||||
const [codexTakeoverEnabled, setCodexTakeoverEnabled] = useState<boolean>(
|
||||
() => codexCatalogCountFromSettings(initialData?.settingsConfig) > 0,
|
||||
);
|
||||
|
||||
const { configError: codexConfigError, debouncedValidate } =
|
||||
useCodexTomlValidation();
|
||||
@@ -616,6 +631,7 @@ function ProviderFormFull({
|
||||
const template = getCodexCustomTemplate();
|
||||
resetCodexConfig(template.auth, template.config);
|
||||
setCodexChatReasoning({});
|
||||
setCodexTakeoverEnabled(false);
|
||||
}
|
||||
}, [appId, initialData, selectedPresetId, resetCodexConfig]);
|
||||
|
||||
@@ -1262,7 +1278,7 @@ function ProviderFormFull({
|
||||
? setCodexWireApi(codexConfig ?? "", "responses")
|
||||
: (codexConfig ?? "");
|
||||
const normalizedCatalogModels =
|
||||
category !== "official" && localCodexApiFormat === "openai_chat"
|
||||
category !== "official" && codexTakeoverEnabled
|
||||
? normalizeCodexCatalogModelsForSave(codexCatalogModels)
|
||||
: [];
|
||||
// Sync first catalog row's model into config.toml so Codex uses it as default
|
||||
@@ -1458,6 +1474,7 @@ function ProviderFormFull({
|
||||
codexChatReasoning:
|
||||
appId === "codex" &&
|
||||
category !== "official" &&
|
||||
codexTakeoverEnabled &&
|
||||
localCodexApiFormat === "openai_chat"
|
||||
? normalizeCodexChatReasoningForSave(codexChatReasoning)
|
||||
: undefined,
|
||||
@@ -1610,6 +1627,8 @@ function ProviderFormFull({
|
||||
codexApiFormatFromWireApi(extractCodexWireApi(template.config)) ??
|
||||
"openai_responses",
|
||||
);
|
||||
// 自定义模板无模型映射,路由默认关闭
|
||||
setCodexTakeoverEnabled(false);
|
||||
}
|
||||
if (appId === "gemini") {
|
||||
resetGeminiConfig({}, {});
|
||||
@@ -1652,6 +1671,8 @@ function ProviderFormFull({
|
||||
codexApiFormatFromWireApi(extractCodexWireApi(config)) ??
|
||||
"openai_responses",
|
||||
);
|
||||
// 路由开关与格式无关,仅按预设是否带模型映射决定
|
||||
setCodexTakeoverEnabled((preset.modelCatalog?.length ?? 0) > 0);
|
||||
|
||||
form.reset({
|
||||
name: preset.nameKey ? t(preset.nameKey) : preset.name,
|
||||
@@ -2124,6 +2145,8 @@ function ProviderFormFull({
|
||||
}
|
||||
autoSelect={endpointAutoSelect}
|
||||
onAutoSelectChange={setEndpointAutoSelect}
|
||||
takeoverEnabled={codexTakeoverEnabled}
|
||||
onTakeoverEnabledChange={setCodexTakeoverEnabled}
|
||||
apiFormat={localCodexApiFormat}
|
||||
onApiFormatChange={handleCodexApiFormatChange}
|
||||
codexChatReasoning={codexChatReasoning}
|
||||
|
||||
Reference in New Issue
Block a user