diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index cbb181e5b..d603b3a40 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -616,7 +616,7 @@ export function CodexFormFields({ const supportsEffort = codexChatReasoning.supportsEffort === true; // 高级区在有任何可见配置时自动展开(仅折叠→展开,不会自动折叠):自定义 UA / - // 请求覆盖 / 已填模型映射 / 原生 Responses(需维护 catalog)/ 已配置思考能力。 + // 请求覆盖 / 非默认的上游格式 / 已配置思考能力。模型列表在高级区外面,不算。 const hasRequestOverrides = Boolean( localProxyHeadersOverride.trim() || localProxyBodyOverride.trim(), ); @@ -624,8 +624,6 @@ export function CodexFormFields({ isCopilotPreset || !!customUserAgent || hasRequestOverrides || - catalogModels.length > 0 || - effectiveApiFormat === "openai_responses" || isAnthropicFormat || supportsThinking || supportsEffort || @@ -1027,8 +1025,8 @@ export function CodexFormFields({ seen.add(id); suggestions.push({ id, - ownedBy: t("codexConfig.modelMappingTitle", { - defaultValue: "模型映射", + ownedBy: t("codexConfig.modelListTitle", { + defaultValue: "模型列表", }), }); } @@ -1827,8 +1825,45 @@ export function CodexFormFields({ {apiKeySection} {endpointSection} + {/* 模型列表 —— 生成 Codex 的模型目录(model_catalog_json):/model 菜单列哪些模型, + 各自的窗口和思考等级。留空不生成。默认模型、子代理从这里选,所以排在它们前面。 */} + {category !== "official" && canEditCatalog && ( +
+
+
+ + {t("codexConfig.modelListTitle", { + defaultValue: "模型列表", + })} + + {renderCatalogActionButtons( + handleAddCatalogRow, + t("codexConfig.addCatalogModel", { + defaultValue: "手动添加", + }), + )} +
+

+ {t("codexConfig.modelListHint", { + defaultValue: + "这些模型会出现在 Codex 的 /model 里,带上各自的上下文窗口和思考等级。修改后需要重启 Codex。", + })} +

+
+ + {fetchedModels.length > 0 && ( + row.model.trim())} + onAdd={handleAddFetchedCatalogRows} + /> + )} + {catalogRows.length > 0 && renderCatalogRows()} +
+ )} + {/* 默认模型 —— config.toml 顶层 model,Codex 启动时默认请求的模型。 - 实时写回 TOML;留空则删行(有映射时保存回退为映射第一行)。 */} + 实时写回 TOML;留空则删行(有模型列表时保存回退为列表第一行)。 */} {category !== "official" && onModelChange && (
@@ -1850,23 +1885,26 @@ export function CodexFormFields({ } className="flex-1" /> - - - + {/* 有模型列表时用列表上的「获取模型列表」,这里不再放一个。 */} + {!canEditCatalog && ( + + + + )} {defaultModelSuggestions.length > 0 && ( {isDefaultModelOutsideCatalog && (

{t("codexConfig.defaultModelNotInCatalog", { defaultValue: - "该模型不在模型映射中,Codex 的 /model 菜单不会列出它(直接请求仍然有效)。", + "该模型不在模型列表中,Codex 的 /model 菜单不会列出它(直接请求仍然有效)。", })}

@@ -1982,7 +2020,7 @@ export function CodexFormFields({

{t("codexConfig.subagentModelNotInCatalog", { defaultValue: - "该模型不在模型映射中:Codex 拿不到它的上下文窗口;经路由转成 Chat / Anthropic 协议时会被换成默认模型。", + "该模型不在模型列表中:Codex 拿不到它的上下文窗口;经路由转成 Chat / Anthropic 协议时会被换成默认模型。", })} {onCatalogModelsChange && ( )} @@ -2002,7 +2040,7 @@ export function CodexFormFields({

)} - {/* 高级选项 —— 上游格式/模型映射/思考能力/自定义 UA;预设供应商通常无需展开 */} + {/* 高级选项 —— 上游格式/思考能力/自定义 UA;预设供应商通常无需展开 */} {category !== "official" && ( )} @@ -2062,58 +2100,10 @@ export function CodexFormFields({ )} - {/* 模型映射 / 模型目录 —— 与「路由接管」解耦,常驻显示(可编辑即渲染)。 - 填了才生成 catalog:Chat 模式生成兼容路由、原生 Responses 生成 - model-catalogs.json;留空则不生成。排在自定义 UA 之前。 */} - {canEditCatalog && ( -
-
-
- - {t("codexConfig.modelMappingTitle", { - defaultValue: "模型映射", - })} - - {renderCatalogActionButtons( - handleAddCatalogRow, - t("codexConfig.addCatalogModel", { - defaultValue: "手动添加", - }), - )} -
-

- {t("codexConfig.modelMappingHint", { - defaultValue: - "选择模型角色后,CC Switch 会自动生成 Codex 兼容路由;菜单显示名可以填 DeepSeek、Kimi 等品牌模型,实际请求模型按右侧填写内容发送。", - })} -

-
- - {fetchedModels.length > 0 && ( - - row.model.trim(), - )} - onAdd={handleAddFetchedCatalogRows} - /> - )} - {catalogRows.length > 0 && renderCatalogRows()} -
- )} -
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index d71b55c62..22c5987af 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1912,17 +1912,15 @@ "modelMetadataAdvancedHint": "Overrides Codex metadata for unknown models; clearing a field removes the matching config.toml entry.", "defaultModelLabel": "Default Model", "defaultModelPlaceholder": "e.g. gpt-5.6-sol", - "defaultModelHint": "The model Codex requests by default; change it anytime without waiting for a preset update. If left empty while model mapping is configured, the first mapping row is used.", - "defaultModelNotInCatalog": "This model is not in the model mapping, so the Codex /model menu won't list it (direct requests still work).", + "defaultModelHint": "The model Codex requests by default; change it anytime without waiting for a preset update. If left empty, the first row of the model list is used.", + "defaultModelNotInCatalog": "This model is not in the model list, so the Codex /model menu won't list it (direct requests still work).", "subagentModelLabel": "Sub-agent Model", "subagentModelPlaceholder": "Leave empty to follow the main model", "subagentReasoningEffortLabel": "Sub-agent reasoning effort", "subagentReasoningEffortDefault": "Default effort", "subagentModelHint": "The model and reasoning effort Codex sub-agents use by default. When empty, sub-agents use the main agent's; with only a model set, they use that model's default effort. Not applied in Aggregation mode, where sub-agents follow the main model.", - "subagentModelNotInCatalog": "This model is not in the model mapping: Codex can't get its context window, and when routing converts requests to Chat / Anthropic it is replaced by the default model.", - "addToModelMapping": "Add to mapping", - "modelMappingTitle": "Model Mapping", - "modelMappingHint": "Generates Codex model_catalog_json so /model can show these third-party model names; entries are saved exactly as listed. Codex must be restarted to refresh the model list after changes.", + "subagentModelNotInCatalog": "This model is not in the model list: Codex can't get its context window, and when routing converts requests to Chat / Anthropic it is replaced by the default model.", + "modelListHint": "These models appear in Codex's /model menu with their own context window and reasoning levels. Restart Codex after changes.", "addCatalogModel": "Add manually", "modelListTitle": "Models", "stackModelListHint": "These models appear in Codex's /model menu; picking one sends requests straight to this provider. Restart Codex after changes.", @@ -1951,7 +1949,7 @@ "reasoningEffortHint": "Enable when the upstream supports thinking-depth control such as low/high/max. Enabling this also turns on thinking mode and converts Codex's reasoning.effort into the upstream Chat parameter.", "reasoningGroupTitle": "Reasoning Capability", "reasoningSectionHint": "Preset providers are configured automatically; custom providers are inferred from name/URL. Override manually only when auto-detection is wrong.", - "advancedSectionHint": "Includes upstream format, model mapping, reasoning overrides and custom User-Agent. Providers using the Chat Completions protocol require routing takeover to be enabled.", + "advancedSectionHint": "Includes upstream format, reasoning overrides and custom User-Agent. Providers using the Chat Completions / Anthropic Messages protocol require routing to be enabled.", "keyFieldsHint": "The endpoint, key, model, reasoning effort, context window and compatibility switches follow the provider; everything else is a global Codex setting that applies to every provider once saved.", "inactiveFieldsHint": "This provider still stores {{count}} settings that are not applied when switching. Click one to copy its TOML and paste it above if you need it; the stored values are kept.", "inactiveFieldCopied": "Copied" diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index d967aa25d..cf9d98a8d 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1912,17 +1912,15 @@ "modelMetadataAdvancedHint": "未知のモデル向けに Codex メタデータを上書きします。空にすると対応する config.toml 設定を削除します。", "defaultModelLabel": "デフォルトモデル", "defaultModelPlaceholder": "例: gpt-5.6-sol", - "defaultModelHint": "Codex がデフォルトでリクエストするモデルです。プリセットの更新を待たずにいつでも変更できます。空欄のままモデルマッピングを設定している場合は、マッピングの先頭行が使われます。", - "defaultModelNotInCatalog": "このモデルはモデルマッピングにないため、Codex の /model メニューには表示されません(直接リクエストは有効です)。", + "defaultModelHint": "Codex がデフォルトでリクエストするモデルです。プリセットの更新を待たずにいつでも変更できます。空欄のときはモデル一覧の先頭行が使われます。", + "defaultModelNotInCatalog": "このモデルはモデル一覧にないため、Codex の /model メニューには表示されません(直接リクエストは有効です)。", "subagentModelLabel": "サブエージェントモデル", "subagentModelPlaceholder": "空欄ならメインモデルに従う", "subagentReasoningEffortLabel": "サブエージェントの推論強度", "subagentReasoningEffortDefault": "デフォルト強度", "subagentModelHint": "Codex が起動するサブエージェントのデフォルトのモデルと推論強度です。空欄ならメインエージェントと同じものを使い、モデルだけ指定するとそのモデルのデフォルト強度を使います。集約モードでは適用されず、サブエージェントはメインモデルに従います。", - "subagentModelNotInCatalog": "このモデルはモデルマッピングにありません。Codex はコンテキストウィンドウを取得できず、ルーティングで Chat / Anthropic 形式に変換する際はデフォルトモデルに置き換えられます。", - "addToModelMapping": "マッピングに追加", - "modelMappingTitle": "モデルマッピング", - "modelMappingHint": "Codex model_catalog_json を生成し、/model コマンドでこれらの第三者モデル名を表示できるようにします。リストの内容はそのまま保存されます。変更後、モデルリストを更新するには Codex の再起動が必要です。", + "subagentModelNotInCatalog": "このモデルはモデル一覧にありません。Codex はコンテキストウィンドウを取得できず、ルーティングで Chat / Anthropic 形式に変換する際はデフォルトモデルに置き換えられます。", + "modelListHint": "これらのモデルは、それぞれのコンテキストウィンドウと思考レベル付きで Codex の /model に表示されます。変更後は Codex を再起動してください。", "addCatalogModel": "手動で追加", "modelListTitle": "モデル一覧", "stackModelListHint": "これらのモデルは Codex の /model に表示され、選ぶとリクエストがこのプロバイダーへ直接送られます。変更後は Codex を再起動してください。", @@ -1951,7 +1949,7 @@ "reasoningEffortHint": "上流が low/high/max などの思考の深さの制御に対応している場合に有効化します。有効にすると思考モードも自動的にオンになり、Codex の reasoning.effort を上流の Chat パラメータに変換します。", "reasoningGroupTitle": "思考能力", "reasoningSectionHint": "プリセットの供給元は自動的に設定され、カスタム供給元は名前/URL から自動推論されます。自動識別が正しくない場合のみ手動で上書きしてください。", - "advancedSectionHint": "上流フォーマット、モデルマッピング、思考能力、カスタム User-Agent の設定を含みます。Chat Completions プロトコルを使用するプロバイダーはルーティング引き継ぎの有効化が必要です。", + "advancedSectionHint": "上流フォーマット、思考能力、カスタム User-Agent の設定を含みます。Chat Completions / Anthropic Messages プロトコルを使用するプロバイダーはルーティングの有効化が必要です。", "keyFieldsHint": "エンドポイント、キー、モデル、推論レベル、コンテキストウィンドウ、互換スイッチはプロバイダーに合わせて切り替わります。それ以外は Codex のグローバル設定で、保存するとすべてのプロバイダーに適用されます。", "inactiveFieldsHint": "このプロバイダーには切り替え時に適用されない設定が {{count}} 件保存されています。クリックすると TOML をコピーできるので、必要なら上に貼り付けてください。保存済みの値は削除されません。", "inactiveFieldCopied": "コピーしました" diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index e95ff14ee..fe7739137 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1907,17 +1907,15 @@ "promptCacheRoutingHint": "自動模式僅對已確認相容的上游傳送 prompt_cache_key;開啟可用於其他相容閘道,關閉可避免嚴格閘道因未知欄位回傳 400。只使用客戶端提供的穩定工作階段 ID。", "defaultModelLabel": "預設模型", "defaultModelPlaceholder": "例如: gpt-5.6-sol", - "defaultModelHint": "Codex 預設請求的模型,隨時可改,無需等待軟體更新供應商範本。留空且設定了模型映射時,預設使用映射第一行。", - "defaultModelNotInCatalog": "該模型不在模型映射中,Codex 的 /model 選單不會列出它(直接請求仍然有效)。", + "defaultModelHint": "Codex 預設請求的模型,隨時可改,無需等待軟體更新供應商範本。留空時使用模型列表第一行。", + "defaultModelNotInCatalog": "該模型不在模型列表中,Codex 的 /model 選單不會列出它(直接請求仍然有效)。", "subagentModelLabel": "子代理模型", "subagentModelPlaceholder": "留空則跟隨主模型", "subagentReasoningEffortLabel": "子代理推理強度", "subagentReasoningEffortDefault": "預設強度", "subagentModelHint": "Codex 派出的子代理預設使用的模型和推理強度。留空時子代理沿用主代理的;只填模型時使用該模型自己的預設強度。聚合模式下不生效,子代理跟隨主模型。", - "subagentModelNotInCatalog": "該模型不在模型映射中:Codex 取得不到它的上下文視窗;經路由轉成 Chat / Anthropic 協定時會被換成預設模型。", - "addToModelMapping": "加入映射", - "modelMappingTitle": "模型映射", - "modelMappingHint": "產生 Codex model_catalog_json,讓 /model 指令顯示這些第三方模型名;表中條目按填寫內容原樣儲存。修改後需要重新啟動 Codex 才能重新整理模型清單。", + "subagentModelNotInCatalog": "該模型不在模型列表中:Codex 取得不到它的上下文視窗;經路由轉成 Chat / Anthropic 協定時會被換成預設模型。", + "modelListHint": "這些模型會出現在 Codex 的 /model 裡,帶上各自的上下文視窗和思考等級。修改後需要重新啟動 Codex。", "addCatalogModel": "手動新增", "modelListTitle": "模型列表", "stackModelListHint": "這些模型會出現在 Codex 的 /model 裡,選中後請求直達這家。修改後需要重新啟動 Codex。", @@ -1952,7 +1950,7 @@ "reasoningEffortHint": "上游支援 low/high/max 等思考深度控制時啟用。啟用後會自動啟用思考模式,並把 Codex 的 reasoning.effort 轉成上游 Chat 參數。", "reasoningGroupTitle": "思考能力", "reasoningSectionHint": "預設供應商已自動設定;自訂供應商會按名稱/位址自動推斷。僅當自動識別不準時才需手動覆寫。", - "advancedSectionHint": "包含上游格式、模型映射、思考能力與自訂 User-Agent。使用 Chat Completions 協定的供應商需開啟路由接管才能使用。", + "advancedSectionHint": "包含上游格式、思考能力與自訂 User-Agent。使用 Chat Completions / Anthropic Messages 協定的供應商需開啟路由才能使用。", "keyFieldsHint": "位址、Key、模型、推理檔位、上下文視窗和相容開關隨供應商切換;其餘是 Codex 全域設定,儲存後對所有供應商生效。", "inactiveFieldsHint": "這個供應商還保存著 {{count}} 個不隨切換生效的設定。點擊可複製它的 TOML,依需要貼到上方;供應商裡保存的原值不會刪除。", "inactiveFieldCopied": "已複製" diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 44005c22b..8c7605b9e 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1912,17 +1912,15 @@ "modelMetadataAdvancedHint": "用于未知模型的 Codex 元数据覆盖;清空字段会从 config.toml 删除对应配置。", "defaultModelLabel": "默认模型", "defaultModelPlaceholder": "例如: gpt-5.6-sol", - "defaultModelHint": "Codex 默认请求的模型,随时可改,无需等待预设更新。留空且配置了模型映射时,默认使用映射第一行。", - "defaultModelNotInCatalog": "该模型不在模型映射中,Codex 的 /model 菜单不会列出它(直接请求仍然有效)。", + "defaultModelHint": "Codex 默认请求的模型,随时可改,无需等待预设更新。留空时用模型列表第一行。", + "defaultModelNotInCatalog": "该模型不在模型列表中,Codex 的 /model 菜单不会列出它(直接请求仍然有效)。", "subagentModelLabel": "子代理模型", "subagentModelPlaceholder": "留空则跟随主模型", "subagentReasoningEffortLabel": "子代理推理强度", "subagentReasoningEffortDefault": "默认强度", "subagentModelHint": "Codex 派出的子代理默认用的模型和推理强度。留空时子代理沿用主代理的;只填模型时用该模型自己的默认强度。聚合模式下不生效,子代理跟随主模型。", - "subagentModelNotInCatalog": "该模型不在模型映射中:Codex 拿不到它的上下文窗口;经路由转成 Chat / Anthropic 协议时会被换成默认模型。", - "addToModelMapping": "加入映射", - "modelMappingTitle": "模型映射", - "modelMappingHint": "生成 Codex model_catalog_json,让 /model 命令显示这些第三方模型名;表中条目按填写内容原样保存。修改后需要重启 Codex 才能刷新模型列表。", + "subagentModelNotInCatalog": "该模型不在模型列表中:Codex 拿不到它的上下文窗口;经路由转成 Chat / Anthropic 协议时会被换成默认模型。", + "modelListHint": "这些模型会出现在 Codex 的 /model 里,带上各自的上下文窗口和思考等级。修改后需要重启 Codex。", "addCatalogModel": "手动添加", "modelListTitle": "模型列表", "stackModelListHint": "这些模型会出现在 Codex 的 /model 里,选中后请求直达这家。修改后需要重启 Codex。", @@ -1951,7 +1949,7 @@ "reasoningEffortHint": "上游支持 low/high/max 等思考深度控制时启用。启用后会自动启用思考模式,并把 Codex 的 reasoning.effort 转成上游 Chat 参数。", "reasoningGroupTitle": "思考能力", "reasoningSectionHint": "预设供应商已自动配置;自定义供应商会按名称/地址自动推断。仅当自动识别不准时才需手动覆盖。", - "advancedSectionHint": "包含上游格式、模型映射、思考能力与自定义 User-Agent。使用 Chat Completions 协议的供应商需开启路由接管才能使用。", + "advancedSectionHint": "包含上游格式、思考能力与自定义 User-Agent。使用 Chat Completions / Anthropic Messages 协议的供应商需开启路由才能使用。", "keyFieldsHint": "地址、Key、模型、推理档位、上下文窗口和兼容开关随供应商切换;其余是 Codex 全局设置,保存后对所有供应商生效。", "inactiveFieldsHint": "这个供应商还保存着 {{count}} 个不随切换生效的设置。点击复制它的 TOML,按需粘贴到上方;供应商里保存的原值不会删除。", "inactiveFieldCopied": "已复制" diff --git a/tests/components/CodexFormFields.subagent.test.tsx b/tests/components/CodexFormFields.subagent.test.tsx index 05b039fe7..534434894 100644 --- a/tests/components/CodexFormFields.subagent.test.tsx +++ b/tests/components/CodexFormFields.subagent.test.tsx @@ -16,7 +16,7 @@ const FormShell = ({ children }: PropsWithChildren) => { return
{children}
; }; -const NOT_IN_CATALOG = /该模型不在模型映射中:Codex 拿不到它的上下文窗口/; +const NOT_IN_CATALOG = /该模型不在模型列表中:Codex 拿不到它的上下文窗口/; function renderCodexForm(overrides: Partial = {}) { const props: CodexFormFieldsProps = { @@ -82,7 +82,7 @@ describe("CodexFormFields sub-agent defaults", () => { expect(subagentInput()).toBeNull(); }); - it("offers to add a model outside the mapping, with the default model when the mapping is empty", () => { + it("offers to add a model outside the list, with the default model when the list is empty", () => { const onCatalogModelsChange = vi.fn(); renderCodexForm({ apiFormat: "openai_chat", @@ -93,7 +93,7 @@ describe("CodexFormFields sub-agent defaults", () => { expect(screen.getByText(NOT_IN_CATALOG)).toBeTruthy(); onCatalogModelsChange.mockClear(); - fireEvent.click(screen.getByRole("button", { name: "加入映射" })); + fireEvent.click(screen.getByRole("button", { name: "加入列表" })); const rows = onCatalogModelsChange.mock.calls.at(-1)?.[0] ?? []; expect(rows.map((row: { model: string }) => row.model)).toEqual([ "gpt-a", @@ -101,8 +101,38 @@ describe("CodexFormFields sub-agent defaults", () => { ]); }); - it("stays quiet for a native Responses provider without a mapping", () => { + it("stays quiet for a native Responses provider without a list", () => { renderCodexForm({ subagentModel: "k3-mini", catalogModels: [] }); expect(screen.queryByText(NOT_IN_CATALOG)).toBeNull(); }); }); + +describe("CodexFormFields direct layout", () => { + it("shows the model list outside the advanced section, before the default model", () => { + renderCodexForm({ + catalogModels: [{ model: "gpt-a" }], + onCatalogModelsChange: vi.fn(), + }); + + const listTitle = screen.getByText("模型列表"); + expect(listTitle).toBeVisible(); + expect(screen.queryByText("上游格式")).toBeNull(); + const defaultModel = document.getElementById("codexDefaultModel")!; + expect( + listTitle.compareDocumentPosition(defaultModel) & + Node.DOCUMENT_POSITION_FOLLOWING, + ).toBeTruthy(); + // 列表上已有「获取模型列表」,默认模型旁不再放第二个。 + expect( + screen.getAllByRole("button", { name: /providerForm\.fetchModels/ }), + ).toHaveLength(1); + }); + + it("keeps the fetch button on the default model when there is no list (Grok Build)", () => { + renderCodexForm(); + expect(screen.queryByText("模型列表")).toBeNull(); + expect( + screen.getByRole("button", { name: "providerForm.fetchModels" }), + ).toBeInTheDocument(); + }); +}); diff --git a/tests/components/GrokBuildProviderForm.test.tsx b/tests/components/GrokBuildProviderForm.test.tsx index 73235b7cc..667944f81 100644 --- a/tests/components/GrokBuildProviderForm.test.tsx +++ b/tests/components/GrokBuildProviderForm.test.tsx @@ -127,6 +127,9 @@ describe("GrokBuildProviderForm", () => { expect(container.querySelector("#grokbuild-context-window")).toHaveValue( 500000, ); + // 默认的 Responses 格式不再把高级区自动展开。 + expect(screen.queryByText("上游格式")).toBeNull(); + fireEvent.click(screen.getByRole("button", { name: /高级选项/ })); expect(screen.getByText("上游格式")).toBeInTheDocument(); }); diff --git a/tests/components/ProviderForm.codexCopilot.test.tsx b/tests/components/ProviderForm.codexCopilot.test.tsx index 1b3f9fb3a..1f42ec30d 100644 --- a/tests/components/ProviderForm.codexCopilot.test.tsx +++ b/tests/components/ProviderForm.codexCopilot.test.tsx @@ -224,7 +224,7 @@ describe("Codex Copilot provider form", () => { expect(screen.getAllByDisplayValue("272000")).toHaveLength(4); expect(screen.getByDisplayValue("200000")).toBeVisible(); expect(formatControl()).toHaveTextContent(formatLabels.auto); - expect(screen.getByText("模型映射")).toBeVisible(); + expect(screen.getByText("模型列表")).toBeVisible(); }); it.each(["auto", "openai_chat", "openai_responses"])( @@ -232,7 +232,7 @@ describe("Codex Copilot provider form", () => { async (format) => { const onSubmit = renderForm(); await selectFormat(format); - expect(screen.getByText("模型映射")).toBeVisible(); + expect(screen.getByText("模型列表")).toBeVisible(); fireEvent.click(screen.getByRole("button", { name: "save" })); await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1)); const saved = onSubmit.mock.calls[0][0]; @@ -490,7 +490,7 @@ describe("Codex Copilot provider form", () => { it("keeps legacy cards automatic and shows mapping even with an empty catalog", () => { renderForm({ providerType: "github_copilot", apiFormat: "openai_chat" }); expect(formatControl()).toHaveTextContent(formatLabels.auto); - expect(screen.getByText("模型映射")).toBeVisible(); + expect(screen.getByText("模型列表")).toBeVisible(); }); it("does not offer preset switching while editing an existing Copilot card", () => { @@ -543,6 +543,8 @@ requires_openai_auth = true`; screen.queryByTestId("selected-copilot-account"), ).not.toBeInTheDocument(); + // 普通 Responses 卡的高级区默认折叠,上游格式在里面。 + fireEvent.click(screen.getByRole("button", { name: /高级选项/ })); await selectFormat("openai_chat"); fireEvent.click(screen.getByRole("button", { name: "save" })); await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1)); @@ -584,7 +586,7 @@ requires_openai_auth = true`; ); fireEvent.click(screen.getByRole("button", { name: /DeepSeek/ })); expect(formatControl()).toHaveTextContent("Responses(原生)"); - expect(screen.getByText("模型映射")).toBeVisible(); + expect(screen.getByText("模型列表")).toBeVisible(); fireEvent.keyDown(formatControl(), { key: "ArrowDown" }); expect( await screen.findByRole("option", { name: /Anthropic Messages/ }), @@ -592,6 +594,6 @@ requires_openai_auth = true`; fireEvent.click(screen.getByRole("option", { name: "Responses(原生)" })); fireEvent.click(screen.getByRole("button", { name: /GitHub Copilot/ })); expect(formatControl()).toHaveTextContent(formatLabels.auto); - expect(screen.getByText("模型映射")).toBeVisible(); + expect(screen.getByText("模型列表")).toBeVisible(); }); }); diff --git a/tests/components/ProviderForm.stackModels.test.tsx b/tests/components/ProviderForm.stackModels.test.tsx index 10e2b904d..520bb7aa1 100644 --- a/tests/components/ProviderForm.stackModels.test.tsx +++ b/tests/components/ProviderForm.stackModels.test.tsx @@ -360,7 +360,8 @@ describe("ProviderForm Stack layout (Codex)", () => { it("没开 Stack 设置时仍是完整表单", () => { settingsState.enableStackMode = false; renderForm("codex", vi.fn()); - expect(screen.queryByText("模型列表")).toBeNull(); + // 完整表单也有模型列表,但说明是直连版的。 + expect(screen.queryByText(/选中后请求直达这家/)).toBeNull(); expect(document.getElementById("codexDefaultModel")).not.toBeNull(); expect(screen.getByTestId("codex-config-editor")).toBeInTheDocument(); });