Hermes WebUI 8787 cpa-codex 模型目录错位复盘
- HTML: https://decision.ht1072.top/2026-06-04-hermes-webui-cpa-codex-model-catalog-retrospective-2026-06-04.html
- Local HTML:
[已移除本地路径] - Generated: 2026-06-04T22:48:29+08:00
Hermes WebUI 8787 cpa-codex 模型目录错位复盘
页面摘要区
核心结论: 本轮调整尚未真正收口。已修复的是 custom:cpa-codex 的默认模型/provider 粘连与同名模型 provider 丢失问题,但模型目录展示仍有策略错误:WebUI 当前把 CPA live aliases 与 config.yaml 中的静态 configured models 当成互斥来源处理,导致 gpt-5.4 / gpt-5.3-codex / gpt-5.2 等静态模型没有显示,同时动态 alias 又未按预期与静态列表做并集。
推荐路径: 先不要宣称 8787 已完全修好;下一步应把 cpa-codex 的模型目录合并策略改成“live aliases ∪ configured models ∪ active/default sticky entries”,并补回归测试覆盖 CPA 动态 alias 与静态模型共存。
关键依据: 当前 ~/.hermes/config.yaml 的 cpa-codex.models 仍包含 gpt-5.4 / gpt-5.5 / gpt-5.4-mini / gpt-5.3-codex / gpt-5.2,但 WebUI get_available_models() 当前 custom:cpa-codex 分组只显示 10 个 live alias / sticky 模型,如 gpt-5.5-high / gpt-5.5-xhigh / claude-opus-4-8 / SC-GPT-5.5(high) / JUN-GPT-5.5(high) / ANY-5.5(high),未显示 gpt-5.4 / gpt-5.3-codex / gpt-5.2。
风险边界: 当前工作区存在 staged 与 unstaged 两层改动,且 api/config.py 的 unstaged 改动把 live catalog 设置为优先来源,和涛哥反馈的“静态模型也要显示”相冲突;不建议直接提交或继续沿该方向扩大修复。
背景
涛哥反馈:8787 WebUI 的对话里选择模型时,cpa-codex 这个 provider 下的“模型显示”和“实际使用”不一致。第一次处理后,我误判为已经调整好;涛哥随后指出仍不对:
cpa-codex里目前没有gpt-5.3-codex、gpt-5.2、gpt-5.4这些模型;- 还有好几个别的模型 alias,也没有按预期显示出来;
- 需要把已做内容整理写入 Wiki 并发布 HTML。
本记录用于留档:明确已做、误判点、当前实际状态、下一步应修方向。
当前时间与环境
- 巡检时间:2026-06-04 22:44:56 CST
- 服务:
hermes-webui-8787.service - 项目路径:
[已移除本地路径] - 当前 workspace:
[已移除本地路径] - WebUI 运行态:8787 服务已重启并处于 active running
- 当前会话模型默认配置:
model.default = SC-GPT-5.5(high),model.provider = custom:cpa-codex
已做内容
1. 后端 provider 路由修正
针对默认模型和同名模型跨 custom provider 的冲突,修改了 api/config.py 中的 resolve_model_provider() 逻辑:
- 当
model.provider = custom:cpa-codex且默认模型是SC-GPT-5.5(high)时,解析结果应保持在custom:cpa-codex; - 对
custom_providers[].models支持 list 形态,不只支持 dict; - 避免同名模型例如
gpt-5.5被后面的其他 custom provider 覆盖。
已观察到的解析结果:
SC-GPT-5.5(high) -> custom:cpa-codex / http://127.0.0.1:8317/v1
gpt-5.5 -> custom:cpa-codex / http://127.0.0.1:8317/v1
gpt-5.4 -> custom:cpa-codex / http://127.0.0.1:8317/v1
gpt-5.3-codex -> custom:cpa-codex / http://127.0.0.1:8317/v1
gpt-5.2 -> custom:cpa-codex / http://127.0.0.1:8317/v1
这部分说明:实际路由层大体已经能把这些模型解析到 cpa-codex。
2. 前端 option provider 上下文修正
修改了 static/ui.js:
populateModelDropdown()生成<option>时写入data-provider;_addLiveModelsToSelect()加 live model 时也写入data-provider;- 目的是避免多个 provider 下有同名模型时,前端只凭
option.value选中第一项,导致 provider 丢失。
这部分解决的是:下拉选项值相同但 provider 不同的歧义。
3. 静态资源缓存 bust
修改了 api/routes.py:
- 新增
_webui_asset_version_token(); - 将静态文件 mtime 纳入
/,/login,/sw.js的版本 token; - 目的:本地热修
static/ui.js/style.css后,移动端/PWA 缓存能更快刷新。
这部分不是模型目录主因,但有助于避免“代码改了前端还用旧 JS”的假阴性。
4. 测试补充
新增或修改了相关测试:
tests/test_custom_provider_default_routing.pytests/test_renderer_js_behaviour.pytests/test_pwa_manifest_sw.pytests/test_issue2540_models_endpoint_error.py中新增了模型目录来源策略相关测试草案
已跑过的窄测试:
pytest -q tests/test_custom_provider_default_routing.py tests/test_renderer_js_behaviour.py
51 passed, 1 warning
这个测试结果只证明默认路由和部分 JS 行为通过,不能证明 cpa-codex 模型目录已完整正确。此前把它当成“已完全修好”的依据,是误判。
当前实际状态
Hermes 配置里的 cpa-codex 静态模型
~/.hermes/config.yaml 中 custom_providers 是 list 形态,cpa-codex 当前配置摘要如下:
model:
default: SC-GPT-5.5(high)
provider: custom:cpa-codex
custom_providers:
- name: cpa-codex
base_url: http://127.0.0.1:8317/v1
api_mode: chat_completions
model: gpt-5.4
models:
- gpt-5.4
- gpt-5.5
- gpt-5.4-mini
- gpt-5.3-codex
- gpt-5.2
- gpt-5.5
说明:从配置源看,gpt-5.4 / gpt-5.3-codex / gpt-5.2 并没有被删除。
WebUI 当前 get_available_models() 展示结果
当前 get_available_models() 返回的 custom:cpa-codex 分组是:
gpt-5.5-high
gpt-5.5-xhigh
claude-opus-4-8
SC-GPT-5.5(high)
JUN-GPT-5.5(high)
gpt-5.4-mini
gpt-5.4-mini-high
ANY-5.5(high)
gpt-5.4-mini-xhigh
gpt-5.5
没有出现:
gpt-5.4
gpt-5.3-codex
gpt-5.2
这与涛哥反馈一致:模型目录展示仍然不完整。
直接访问 CPA /v1/models 的限制
裸请求:
GET http://127.0.0.1:8317/v1/models
返回:
HTTP 401 Unauthorized
说明:不能用不带鉴权的裸 curl / urllib 结果判断 CPA 实际模型目录。WebUI 内部能读到 live aliases,是因为它走了 custom provider connection / api key 解析链路。
根因判断
当前问题不是单一 bug,而是两类模型来源没有统一:
- configured models:来自
~/.hermes/config.yaml的custom_providers[].models,代表本地静态配置/用户声明; - live aliases:来自 CPA
/v1/models的动态 alias,代表 CPA 当前可暴露模型; - sticky default/active model:来自
model.default+model.provider,必须保证当前默认模型可见。
此前 unstaged 修改把策略改成:
健康 live catalog 优先;configured models 只作为 fallback/sticky。
这会导致当前现象:当 CPA live catalog 返回成功时,静态配置里的 gpt-5.4 / gpt-5.3-codex / gpt-5.2 不再进入下拉列表。
涛哥当前期望更接近:
cpa-codex 下拉列表 = live aliases ∪ configured models ∪ active/default sticky entries
也就是:不是 live 替代 config,也不是 config 替代 live,而是要合并,并稳定去重。
当前工作区状态
仓库路径:[已移除本地路径]
当前存在 staged 与 unstaged 两层改动:
staged 文件
CHANGELOG.md
api/config.py
api/routes.py
static/style.css
static/ui.js
tests/test_custom_provider_default_routing.py
tests/test_pwa_manifest_sw.py
tests/test_renderer_js_behaviour.py
unstaged 文件
api/config.py
static/ui.js
tests/test_issue2540_models_endpoint_error.py
重要提醒
api/config.py 的 unstaged 部分包含“live catalog wins over stale configured models”的方向,这与当前反馈冲突。后续修复前建议不要直接提交这层 unstaged 改动,应先改为 union 策略或回退再重做。
建议下一步修复策略
推荐策略
把 get_available_models() 中 named custom provider 的模型合并逻辑改成:
1. 读取 live /v1/models,得到 live aliases;
2. 读取 custom_providers[].model 与 custom_providers[].models,得到 configured models;
3. 读取 model.default + model.provider,得到 sticky default;
4. 对同一 provider 内按 id 去重,顺序建议:
live aliases 优先展示;
sticky default 如果不在 live/config 中则插入靠前;
configured models 补到后面;
5. 除非显式配置 model_catalog/models_source = configured/static/manual/allowlist,才禁止 live probe 或只展示配置清单。
建议补的回归测试
至少补三组:
-
live + configured 并集 - live 返回:
gpt-5.5-high / SC-GPT-5.5(high) / ANY-5.5(high); - config 包含:gpt-5.4 / gpt-5.3-codex / gpt-5.2; - 期望下拉同时包含两边。 -
configured catalog opt-in - 当 provider 写
model_catalog: configured或models_source: configured时; - 不 probe/v1/models,只展示 configured models + sticky default。 -
同名模型 provider 不丢失 -
custom:cpa-codex与custom:yt都有gpt-5.5; - 选择 cpa-codex 下的gpt-5.5后,发送 payload 必须带model_provider=custom:cpa-codex。
风险与边界
风险 1:继续把 live 与 configured 做互斥会反复漏模型
CPA 这类 aggregator / proxy gateway 的 alias 可能动态变化,配置里的 models 又代表用户主动声明。二者互斥会导致一边总是缺失。
风险 2:直接提交当前 unstaged 改动会固化错误策略
当前 api/config.py unstaged 改动已经明显倾向 live catalog 优先、configured fallback。这正是当前缺 gpt-5.4 / gpt-5.3-codex / gpt-5.2 的原因之一。
风险 3:只看 200 OK 或窄测试会误判
51 passed 只覆盖路由和 JS 行为,不能覆盖完整模型目录源合并。验收必须包含 get_available_models() 的 provider 分组内容。
风险 4:裸访问 CPA /v1/models 会因鉴权失败产生假结论
CPA /v1/models 裸请求返回 401。后续要验证 live aliases,应走 WebUI/Hermes 内部 provider connection 或带正确 API key 的请求。
验收标准
修复完成后,至少满足:
get_available_models()的custom:cpa-codex分组同时包含: - live aliases:如gpt-5.5-high / gpt-5.5-xhigh / SC-GPT-5.5(high) / JUN-GPT-5.5(high) / ANY-5.5(high); - configured models:gpt-5.4 / gpt-5.3-codex / gpt-5.2 / gpt-5.4-mini / gpt-5.5。- 前端下拉每个 option 都带正确
data-provider。 - 发送消息 payload 中携带
model_provider=custom:cpa-codex。 - 服务重启后 8787 日志中 reasoning/chat 请求仍为:
-
model=<用户选择的模型>; -provider=custom:cpa-codex。 - 回归测试覆盖 live + configured union,而不是只覆盖默认路由。
行动清单
- 先把当前 unstaged 的 live-only 策略改成 union 策略,或回退后重做。
- 补
test_named_custom_provider_live_catalog_merges_configured_models,明确验证 live aliases 与 configured models 并集。 - 保留
data-provider前端修正,继续防止同名模型 provider 丢失。 - 修完后重启
hermes-webui-8787.service,并用 fresh Python process 验证get_available_models()。 - 最终验收不能只看测试通过,要打印
custom:cpa-codex分组完整模型列表。
完整记录
本轮主要事实:
- 已修的方向:默认模型/provider 路由、前端 option provider 上下文、静态资源缓存 bust。
- 误判的地方:把路由修复 + 窄测试通过当成“模型目录完全正确”。
- 当前真实问题:
get_available_models()的模型目录来源合并策略仍不对。 - 当前建议:将
cpa-codex目录改成 live aliases、configured models、sticky default 的稳定并集。
本记录不是最终修复报告,而是阶段复盘和后续修复指引。