Self Checks / 自检
CC Switch Version / 版本号
3.20.2-19
Operating System / 操作系统
macOS
Related App / 涉及应用
Codex
Steps to Reproduce / 重现步骤
环境
- macOS + CCSwitchMulti
3.20.2-19,Codex MultiRouter(codexRouting.schemaVersion = 2)已接管 Codex。
- 只接入公司的 OpenAI-compatible 网关(下称
https://<company-gateway>/v1),没有把官方 OpenAI Provider 加入任何 route。
- 公司 Provider 的显示名是
gpt5.6,其 modelCatalog 只有 3 个模型:gpt-5.6-luna、gpt-5.6-sol、gpt-5.6-terra。
- 保存后该 Provider 的 route 配置如下(脱敏):
{
"id": "router-fa1f8632-…",
"label": "gpt5.6",
"enabled": true,
"targetProviderId": "fa1f8632-…",
"modelSelection": { "mode": "all" },
"matchPrefixes": ["gpt"],
"aliases": {
"gpt-5.6-luna-gpt5-6": "gpt-5.6-luna",
"gpt-5.6-sol-gpt5-6": "gpt-5.6-sol",
"gpt-5.6-terra-gpt5-6": "gpt-5.6-terra"
},
"authPolicy": { "source": "provider_config" }
}
重现步骤
- 用「配置多路模型」向导只保留公司 Provider 作为模型源(不勾选官方 Provider),生成 route,模型选择范围选 全部模型(自动接收新增)。
- 保存并让 Codex 重新读取模型目录后,打开 CCSwitchMulti 的「Codex 模型排序」:同一个模型会出现两条,显示名完全相同,只有括号里的模型 ID 不同:
1 gpt-5.6-sol (gpt-5.6-sol)
2 gpt-5.6-sol (gpt-5.6-sol-gpt5-6)
3 gpt-5.6-terra (gpt-5.6-terra)
4 gpt-5.6-terra (gpt-5.6-terra-gpt5-6)
5 gpt-5.6-luna (gpt-5.6-luna)
6 gpt-5.6-luna (gpt-5.6-luna-gpt5-6)
- 点击其中任意一条的「移除」:两条会被同时移除,无法只删掉别名那一条,也无法单独保留。
- 把同一条 route 的「模型选择范围」改成 仅选中的上游模型,勾选这 3 个模型并保存后:Codex 侧这 3 个模型全部不可用,请求返回 400。
- 打开「子 Agent」页面:同样会出现两套条目 ——
gpt-5.6-luna / gpt-5.6-sol / gpt-5.6-terra 与 gpt-5.6-luna-gpt5-6 / gpt-5.6-sol-gpt5-6 / gpt-5.6-terra-gpt5-6,共 6 条,其中一套显示为「待配置 / 未启用」。
Expected Behavior / 期望行为
- 同一个 canonical 模型(无论是否带可见别名)在「Codex 模型排序」和 Codex 模型菜单里只应出现一条,或至少在 UI 上明确标注它们指向同一个上游模型。
- 删除某个可见模型时只应删除它自己,不应连带删除同一 canonical 模型的另一个条目。
- 「仅选中的上游模型」(
modelSelection.mode = include)应当和 all 一样可用,不应该出现模型全部不可用 / 400 的情况。
- 子 Agent 页面不应长期同时保留 canonical 与别名两套 profile;重名冲突消失(例如官方 Provider 已不再参与路由)后,自动生成的别名应能收敛或清理,而不是永久留在 route contract 里。
- 自动别名的后缀来自 Provider 显示名,Provider 改名会改变 Codex 侧可见模型 ID;期望改名后旧 ID 不残留在模型排序列表、
spawnAgentModels 和子 Agent profile 中。
Actual Behavior / 实际行为
- 「全部模型(自动接收新增)」:每个模型显示两条(canonical ID 与
-gpt5-6 别名 ID),显示名相同。
- 删除其中一条时两条一起消失。
- 「仅选中的上游模型」:模型完全不可用,请求返回 400。
- 子 Agent 页面:canonical 与别名两套条目并存,共 6 条。
Additional Context / 补充信息
1. 别名是自动生成的重名别名,后缀来自 Provider 显示名
主分支 src/lib/codexMultiRouterWizard.ts 中:
providerNameSuffix() 把 Provider 显示名清理成模型 ID 后缀:gpt5.6 → gpt5-6;
resolveWizardModelNameCollisions() 在多个模型源暴露同名模型时,官方源保留原名、非官方源生成 <model>-<providerNameSuffix> 可见别名,并把 upstreamModel 指向真实上游模型名;
buildWizardRoutesFromSources() 会把已有的 route.aliases 作为 route 契约长期保留(注释:「A generated collision alias becomes part of the route contract on first save.」)。
因此当官方 Provider 已经不再参与路由、重名冲突实际已经消失后,旧别名仍然留在 route 里,于是同一个上游模型在 catalog 里同时存在 canonical 与别名两个可见 ID。
2. 本机生成的目录文件同样只写入了别名 ID
~/.codex/cc-switch-model-catalog.json(脱敏,仅保留关键字段):
[
{ "model": "gpt-5.6-sol-gpt5-6", "displayName": "gpt-5.6-sol" },
{ "model": "gpt-5.6-terra-gpt5-6", "displayName": "gpt-5.6-terra" },
{ "model": "gpt-5.6-luna-gpt5-6", "displayName": "gpt-5.6-luna" }
]
即 Codex 侧可见模型 ID 已经是别名,而「Codex 模型排序」还会把公司 Provider 目录里的 canonical gpt-5.6-sol 一起列出来,形成重复行。
3. 子 Agent profile 与 spawnAgentModels 存在多代残留
codexRouting.subagentV2.profiles 中同时存在:
gpt-5.6-luna (enabled=false) / gpt-5.6-luna-gpt5-6 (enabled=true)
gpt-5.6-sol (enabled=false) / gpt-5.6-sol-gpt5-6 (enabled=true)
gpt-5.6-terra(enabled=false) / gpt-5.6-terra-gpt5-6(enabled=true)
spawnAgentModels 里还残留上两代别名:gpt-5.6-sol-longnows-5-6 等(见下条)。
4. 由于后缀跟随 Provider 显示名,历史上已产生三代可见 ID
本地 codex-router.log 中同一 Provider 的可见模型 ID 依次为:
gpt-5.6-sol-longnows 首次出现 2026-07-17
gpt-5.6-sol-longnows-5-6 首次出现 2026-07-24
gpt-5.6-sol-gpt5-6 首次出现 2026-09-20
说明 Provider 每次改名(longnows → longnows 5.6 → gpt5.6)都会生成新的可见模型 ID,旧 ID 不会自动清理。
5. 400 的日志证据(脱敏)
codex-router.log 中该 route 共 66 次 status=200、3 次 status=400:
2026-09-20 09:15:44 model=gpt-5.6-sol status=400 body_summary=Upstream_request_failed
2026-09-20 09:18:09 model=gpt-5.6-sol status=400 body_summary=Upstream_request_failed
2026-09-20 09:24:05 model=gpt-5.6-sol-gpt5-6 status=400 body_summary=Upstream_request_failed
这 3 条都已经走到 upstream_url=https://<company-gateway>/v1/responses(route_missed=false),不是本地路由未命中;不过我没有保存 Codex 端完整的 400 响应体,无法确认它与「仅选中的上游模型」是否是同一个失败原因,如果需要我可以补充完整报错文本和截图。
6. 相关代码位置(main 分支)
src/lib/codexMultiRouterWizard.ts
providerNameSuffix() / aliasModelName() / resolveWizardModelNameCollisions():自动别名的生成规则;
buildWizardRoutesFromSources():已有 alias 长期保留,冲突消失后也不会清理;
buildSubagentNameRedirector() / remapSubagentV2():旧可见名 → 新可见名的重定向(当前保留了新旧两套 profile)。
src/components/codex/CodexMultiRouterWizard.tsx、src/components/codex/CodexRouterWorkspacePage.tsx:向导与工作台对重名别名的展示/选择。
7. 隐私说明
本 issue 不包含 API Key、Token、Cookie、认证头、完整请求体或数据库备份;公司网关域名已用 https://<company-gateway>/v1 占位。截图(Codex 模型排序重复行、route 编辑页、子 Agent 页面重复条目)可按需补充。
Self Checks / 自检
I have read the FAQ section in README.
我已阅读 README 中的常见问题。
I have searched for existing issues, including closed ones.
我已搜索过已有的 Issue,包括已关闭的。
CC Switch Version / 版本号
3.20.2-19
Operating System / 操作系统
macOS
Related App / 涉及应用
Codex
Steps to Reproduce / 重现步骤
环境
3.20.2-19,Codex MultiRouter(codexRouting.schemaVersion = 2)已接管 Codex。https://<company-gateway>/v1),没有把官方 OpenAI Provider 加入任何 route。gpt5.6,其modelCatalog只有 3 个模型:gpt-5.6-luna、gpt-5.6-sol、gpt-5.6-terra。{ "id": "router-fa1f8632-…", "label": "gpt5.6", "enabled": true, "targetProviderId": "fa1f8632-…", "modelSelection": { "mode": "all" }, "matchPrefixes": ["gpt"], "aliases": { "gpt-5.6-luna-gpt5-6": "gpt-5.6-luna", "gpt-5.6-sol-gpt5-6": "gpt-5.6-sol", "gpt-5.6-terra-gpt5-6": "gpt-5.6-terra" }, "authPolicy": { "source": "provider_config" } }重现步骤
gpt-5.6-luna/gpt-5.6-sol/gpt-5.6-terra与gpt-5.6-luna-gpt5-6/gpt-5.6-sol-gpt5-6/gpt-5.6-terra-gpt5-6,共 6 条,其中一套显示为「待配置 / 未启用」。Expected Behavior / 期望行为
modelSelection.mode = include)应当和all一样可用,不应该出现模型全部不可用 / 400 的情况。spawnAgentModels和子 Agent profile 中。Actual Behavior / 实际行为
-gpt5-6别名 ID),显示名相同。Additional Context / 补充信息
1. 别名是自动生成的重名别名,后缀来自 Provider 显示名
主分支
src/lib/codexMultiRouterWizard.ts中:providerNameSuffix()把 Provider 显示名清理成模型 ID 后缀:gpt5.6→gpt5-6;resolveWizardModelNameCollisions()在多个模型源暴露同名模型时,官方源保留原名、非官方源生成<model>-<providerNameSuffix>可见别名,并把upstreamModel指向真实上游模型名;buildWizardRoutesFromSources()会把已有的route.aliases作为 route 契约长期保留(注释:「A generated collision alias becomes part of the route contract on first save.」)。因此当官方 Provider 已经不再参与路由、重名冲突实际已经消失后,旧别名仍然留在 route 里,于是同一个上游模型在 catalog 里同时存在 canonical 与别名两个可见 ID。
2. 本机生成的目录文件同样只写入了别名 ID
~/.codex/cc-switch-model-catalog.json(脱敏,仅保留关键字段):[ { "model": "gpt-5.6-sol-gpt5-6", "displayName": "gpt-5.6-sol" }, { "model": "gpt-5.6-terra-gpt5-6", "displayName": "gpt-5.6-terra" }, { "model": "gpt-5.6-luna-gpt5-6", "displayName": "gpt-5.6-luna" } ]即 Codex 侧可见模型 ID 已经是别名,而「Codex 模型排序」还会把公司 Provider 目录里的 canonical
gpt-5.6-sol一起列出来,形成重复行。3. 子 Agent profile 与 spawnAgentModels 存在多代残留
codexRouting.subagentV2.profiles中同时存在:spawnAgentModels里还残留上两代别名:gpt-5.6-sol-longnows-5-6等(见下条)。4. 由于后缀跟随 Provider 显示名,历史上已产生三代可见 ID
本地
codex-router.log中同一 Provider 的可见模型 ID 依次为:说明 Provider 每次改名(
longnows→longnows 5.6→gpt5.6)都会生成新的可见模型 ID,旧 ID 不会自动清理。5. 400 的日志证据(脱敏)
codex-router.log中该 route 共 66 次status=200、3 次status=400:这 3 条都已经走到
upstream_url=https://<company-gateway>/v1/responses(route_missed=false),不是本地路由未命中;不过我没有保存 Codex 端完整的 400 响应体,无法确认它与「仅选中的上游模型」是否是同一个失败原因,如果需要我可以补充完整报错文本和截图。6. 相关代码位置(main 分支)
src/lib/codexMultiRouterWizard.tsproviderNameSuffix()/aliasModelName()/resolveWizardModelNameCollisions():自动别名的生成规则;buildWizardRoutesFromSources():已有alias长期保留,冲突消失后也不会清理;buildSubagentNameRedirector()/remapSubagentV2():旧可见名 → 新可见名的重定向(当前保留了新旧两套 profile)。src/components/codex/CodexMultiRouterWizard.tsx、src/components/codex/CodexRouterWorkspacePage.tsx:向导与工作台对重名别名的展示/选择。7. 隐私说明
本 issue 不包含 API Key、Token、Cookie、认证头、完整请求体或数据库备份;公司网关域名已用
https://<company-gateway>/v1占位。截图(Codex 模型排序重复行、route 编辑页、子 Agent 页面重复条目)可按需补充。