Hermes Decision Trace

CLIProxyAPI OpenAI-compatible `(high)` alias auth_not_found 修复与 8317 systemd 正式化

cpa-codex / CLIProxyAPI 的 OpenAI-compatible alias 在官方 v7.1.47 和官方 v7.1.50 下都存在真实运行时缺陷:形如 YT-GPT-5.5(high) 的模型虽然能在 /v1/models 列出来,但 chat 请求会在 0ms 内被本地 runtime 过滤掉,返回 auth_not_found。本次已在本机基于最新 v7.1.50 做最小修复,并已切到 live 8317

🧭
推荐路径

保留修复版作为当前 live;不要用“去掉 (high) alias”作为长期方案。该 workaround 只能绕过问题,不能修复 runtime 行为,也会让模型名语义和配置口径变得不稳定。下一步应把这个最小补丁整理成面向官方的 PR,只提交通用代码和测试,不带任何本地隐私配置。

🔎
关键依据

官方 v7.1.50 staging 在 18317 上复现:/v1/models 200,但 YT/ANY/JUN alias chat 都是 503 auth_not_found,且耗时 0ms;本地修复后,在 staging 18317 和 live 8317 上都验证通过:/v1/models 200,YT-GPT-5.5(high) / YT-GPT-5.4(high) / ANY-5.5(high) / JUN-GPT-5.5(high) 非流式返回 200,流式也返回 200;8317 现已纳入 systemd --user 服务 cli-proxy-api-8317.service 正式托管,不再依赖手工前台/后台进程。

🛠️
落地方式

当前先把修复版作为稳定 live 基线,保留 8317 systemd 托管;对外只推进通用补丁和 regression test,不把本机 alias 命名、私有 provider 配置或备份路径带进官方 PR。

证据摘要

  • 官方 v7.1.50 staging 在 18317 上复现:/v1/models 200,但 YT/ANY/JUN alias chat 都是 503 auth_not_found,且耗时 0ms;本地修复后,在 staging 18317 和 live 8317 上都验证通过:/v1/models 200,YT-GPT-5.5(high) / YT-GPT-5.4(high) / ANY-5.5(high) / JUN-GPT-5.5(high) 非流式返回 200,流式也返回 200;8317 现已纳入 systemd --user 服务 cli-proxy-api-8317.service 正式托管,不再依赖手工前台/后台进程。

行动清单

保持修复版 live 运行,继续观察 alias 调用稳定性。
将本次通用修复整理为无隐私的官方 PR。
不把 workaround 当长期方案,不删除 (high) alias 语义来掩盖 runtime bug。

边界 / 风险

风险与边界

当前 live 使用的是基于官方 v7.1.50 的本地修复版,不是官方原版二进制。 本地 OpenAI-compatible provider 名、别名命名、真实上游和 key 都属于隐私/环境特定信息,不能直接带进官方 PR。 ANYmax_tokens=8 时会返回上游 400 integer_below_min_value,这不是 auth 调度故障;验证时需要给足 token。

完整记录

CLIProxyAPI OpenAI-compatible (high) alias auth_not_found 修复与 8317 systemd 正式化

证据摘要

  • 官方 v7.1.50 原版在 staging 18317 可稳定复现 auth_not_found,说明不是 WebUI 或管理端误报。
  • 修复版在 staging 18317 与 live 8317 都通过 /v1/models、non-stream、stream 三层验证。
  • live 当前由 cli-proxy-api-8317.service 托管,cpa-manager-plus.service 仍保持在 8318,控制面未被这次修复扰动。

下一步

  1. 保持修复版 live 运行,继续观察 alias 调用稳定性。
  2. 将本次通用修复整理为无隐私的官方 PR。
  3. 不把 workaround 当长期方案,不删除 (high) alias 语义来掩盖 runtime bug。

风险与边界

  • 当前 live 使用的是基于官方 v7.1.50 的本地修复版,不是官方原版二进制。
  • 本地 OpenAI-compatible provider 名、别名命名、真实上游和 key 都属于隐私/环境特定信息,不能直接带进官方 PR。
  • ANYmax_tokens=8 时会返回上游 400 integer_below_min_value,这不是 auth 调度故障;验证时需要给足 token。

背景

涛哥反馈:cpa-codex 里多个模型“都调用不通了”。前置排查确认:

  • 8317CLIProxyAPI 数据面;8318CPA-Manager-Plus 管理面。
  • /v1/models 依然能列出 YT-GPT-5.5(high)YT-GPT-5.4(high)ANY-5.5(high)JUN-GPT-5.5(high) 这些 alias。
  • 但 chat completions 在本地就 0ms 失败,没有进入真实上游。

因此问题不在 WebUI,不在 Hermes provider catalog,也不在 CPA-Manager-Plus 控制面,而在 CLIProxyAPI runtime 自身的 auth/model candidate 过滤逻辑。

根因拆解

1. 真正击穿 live 的主因:suffixed alias 与 registry 查询名不一致

运行路径里,authSupportsRouteModel() 会用 canonicalModelKey(routeModel) 去判断某个 auth 是否支持请求模型;而 canonicalModelKey() 会把 (high) 这样的 thinking suffix 剥掉。

这就造成了错位:

  • registry 注册的是用户可见 alias,例如 YT-GPT-5.5(high)
  • 调度查询时却变成 yt-gpt-5.5
  • 两边对不上,candidate auth 在真正调用前就被筛空;
  • 最终表现为 auth_not_found,而且因为没打上游,所以耗时接近 0ms。

2. 次级防御性问题:executor provider key 大小写/规范化不一致

OpenAI-compatible executor 和 auth/provider 名可能来自不同入口。为了避免 YT / ytAnyRouter-Adapter / anyrouter-adapter 这种大小写差异在 legacy path 或 fallback path 里继续踩坑,本次顺手把 executor map 的注册和查询都收口为同一个 trim+lowercase helper。

这个问题不是本次 live 故障唯一主因,但不修会留下隐性炸点。

修复方案

代码修复

sdk/cliproxy/auth/conductor.go 中做了两类最小改动:

  1. 新增统一的 executorProviderKey(provider string),把 RegisterExecutor()Executor()UnregisterExecutor()、legacy pick、refresh、credential inject 等路径全部收口到同一 provider key 规范。
  2. 调整 authSupportsRouteModel()
  • 先按原始 routeModel 查 registry;
  • 原始 alias 不命中时,再 fallback 到 canonical model key 和 selection key。

同时补了回归测试:

  • TestManagerExecutorReturnsRegisteredExecutor
  • TestManagerMixedLegacyFindsExecutorWithCanonicalProviderKey

后者覆盖了“大写 executor id + 小写 auth provider + (high) alias route model”的实际故障形态。

运行方式修复

8317 从手工进程切到 systemd --user

  • unit: ~/.config/systemd/user/cli-proxy-api-8317.service
  • cwd: /home/ht/cli-proxy-api
  • ExecStart: /home/ht/cli-proxy-api/cli-proxy-api

这样后续重启、开机恢复、观察和回滚都更稳,不会再靠临时 shell 进程维持 live。

验证结果

官方 v7.1.50 staging 复现

18317 上使用官方二进制复现到:

/v1/models -> 200 YT-GPT-5.5(high) -> 503 auth_not_found (0ms) YT-GPT-5.4(high) -> 503 auth_not_found (0ms) ANY-5.5(high) -> 503 auth_not_found (0ms) JUN-GPT-5.5(high) -> 503 auth_not_found (0ms) stream -> 503 auth_not_found (0ms)

修复版 staging 验证

修复版切到 18317 后:

/v1/models -> 200 YT-GPT-5.5(high) non-stream -> 200 pong YT-GPT-5.4(high) non-stream -> 200 pong JUN-GPT-5.5(high) non-stream -> 200 pong ANY-5.5(high) non-stream -> 200 pong (max_tokens=64) stream path -> 200 chunks

live 8317 验证

切 live 并接入 systemd 后再次实测:

/v1/models -> 200 YT-GPT-5.5(high) non-stream -> 200 pong YT-GPT-5.4(high) non-stream -> 200 pong ANY-5.5(high) non-stream -> 200 pong JUN-GPT-5.5(high) non-stream -> 200 pong YT-GPT-5.5(high) stream -> 200 chunks

当前运行态

  • live data-plane: 127.0.0.1:8317
  • live unit: cli-proxy-api-8317.service
  • manager sidecar: cpa-manager-plus.service at 127.0.0.1:8318
  • live fixed binary sha256: 8563eefec0d2af655bed7babf60577e9c0ca0f77cb176b63afe45337eff7f0d7
  • pre-cutover backup: /home/ht/cli-proxy-api/backups/pre-v7150-canonical-executor-20260607-174924

采取的动作

  1. 复现官方 v7.1.50 staging 故障。
  2. 在本地源码中修复 runtime candidate 过滤。
  3. 增加 focused regression test。
  4. 编译修复版并在 18317 staging 验证非流式和流式。
  5. 备份 live 8317 二进制和配置。
  6. 将修复版切到 live 8317
  7. 8317 纳入 systemd --user 正式托管。
  8. 再次进行 live API 验证。
  9. 整理本次排障经验,准备 wiki/HTML 与上游 PR。

回滚与边界

回滚

如需回退到切换前二进制:

systemctl --user stop cli-proxy-api-8317.service cp /home/ht/cli-proxy-api/backups/pre-v7150-canonical-executor-20260607-174924/cli-proxy-api /home/ht/cli-proxy-api/cli-proxy-api chmod +x /home/ht/cli-proxy-api/cli-proxy-api systemctl --user start cli-proxy-api-8317.service systemctl --user status cli-proxy-api-8317.service --no-pager ss -ltnp 'sport = :8317'

上游 PR 边界

官方 PR 只提交:

  • conductor.go 的通用修复;
  • 对应 regression tests;
  • 干净的 PR 描述与复现方式。

官方 PR 不提交:

  • 本机 config.yaml
  • 任何 key、token、base URL
  • 本机路径、backup/staging 目录
  • HTML、wiki、排障日志原文
  • 私有 provider 命名或供应商配置细节

长期经验

  1. /v1/models 正常不代表 chat 路径正常,尤其是 alias + routing + auth 三层可能在 runtime 才断。
  2. 带 suffix 的 alias 要同时核对“registry 注册名”和“调度查询名”。
  3. OpenAI-compatible alias 故障必须同时测 /v1/models、non-stream、stream,不能只测一个文本 200。
  4. 类似 8317 这种 live 数据面要尽快 systemd 化,避免“功能修好了但运行态不可控”。
  5. 对官方提 PR 时,要把“本机问题”压缩为“通用 runtime 行为缺陷”,否则容易把隐私、环境噪音和产品口径绑进 patch。