Hermes Decision Trace

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.yamlcpa-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

🛠️
落地方式

先把已验证方案当成稳定基线:保留当前 schedule / deliver / workdir,不急着继续扩面;新增候选先读源码、看 output、做 run-now 验证,再决定是否转 script-only。

证据摘要

  • 当前 ~/.hermes/config.yamlcpa-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

行动清单

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: configuredmodels_source: configured 时;

边界 / 风险

继续把 live 与 configured 做互斥会反复漏模型

CPA 这类 aggregator / proxy gateway 的 alias 可能动态变化,配置里的 models 又代表用户主动声明。二者互斥会导致一边总是缺失。

直接提交当前 unstaged 改动会固化错误策略

当前 api/config.py unstaged 改动已经明显倾向 live catalog 优先、configured fallback。这正是当前缺 gpt-5.4 / gpt-5.3-codex / gpt-5.2 的原因之一。

只看 200 OK 或窄测试会误判

51 passed 只覆盖路由和 JS 行为,不能覆盖完整模型目录源合并。验收必须包含 get_available_models() 的 provider 分组内容。

裸访问 CPA /v1/models 会因鉴权失败产生假结论

CPA /v1/models 裸请求返回 401。后续要验证 live aliases,应走 WebUI/Hermes 内部 provider connection 或带正确 API key 的请求。

完整记录

Hermes WebUI 8787 cpa-codex 模型目录错位复盘

背景

涛哥反馈:8787 WebUI 的对话里选择模型时,cpa-codex 这个 provider 下的“模型显示”和“实际使用”不一致。第一次处理后,我误判为已经调整好;涛哥随后指出仍不对:

  • cpa-codex 里目前没有 gpt-5.3-codexgpt-5.2gpt-5.4 这些模型;
  • 还有好几个别的模型 alias,也没有按预期显示出来;
  • 需要把已做内容整理写入 Wiki 并发布 HTML。

本记录用于留档:明确已做、误判点、当前实际状态、下一步应修方向。

当前时间与环境

  • 巡检时间:2026-06-04 22:44:56 CST
  • 服务:hermes-webui-8787.service
  • 项目路径:/home/ht/.hermes/external-projects/hermes-webui
  • 当前 workspace:/home/ht/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.py
  • tests/test_renderer_js_behaviour.py
  • tests/test_pwa_manifest_sw.py
  • tests/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.yamlcustom_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,而是两类模型来源没有统一:

  1. configured models:来自 ~/.hermes/config.yamlcustom_providers[].models,代表本地静态配置/用户声明;
  2. live aliases:来自 CPA /v1/models 的动态 alias,代表 CPA 当前可暴露模型;
  3. 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,而是要合并,并稳定去重。

当前工作区状态

仓库路径:/home/ht/.hermes/external-projects/hermes-webui

当前存在 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 或只展示配置清单。

建议补的回归测试

至少补三组:

  1. 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
  • 期望下拉同时包含两边。
  1. configured catalog opt-in
  • 当 provider 写 model_catalog: configuredmodels_source: configured 时;
  • 不 probe /v1/models,只展示 configured models + sticky default。
  1. 同名模型 provider 不丢失
  • custom:cpa-codexcustom: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 的请求。

验收标准

修复完成后,至少满足:

  1. 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
  1. 前端下拉每个 option 都带正确 data-provider
  2. 发送消息 payload 中携带 model_provider=custom:cpa-codex
  3. 服务重启后 8787 日志中 reasoning/chat 请求仍为:
  • model=<用户选择的模型>
  • provider=custom:cpa-codex
  1. 回归测试覆盖 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 的稳定并集。

本记录不是最终修复报告,而是阶段复盘和后续修复指引。