Files
ModelRouter/docs/channel-issues-2026-10-19.md
root 2113bfc125 fix(channels): 四渠道修复 —— qoder 裸 token 领取 / trae+buddy 非流式 / opencode 目录 / 隐藏适配器
生产实测逐渠道排查(2026-10-19):

* qoder:PKCE 设备码流无 register 第二跳,落库凭据是裸 token 字符串;
  qoderClaim 硬要求结构化 table 导致「缺少凭据对象」(而 account_test
  走 token 路径正常 —— 自相矛盾状态)。改为非空裸 tok 就地包
  {access_token=tok};补 TestQoderClaimAcceptsBareTokenCredential。

* trae(SOLO 自装适配器):上游对非流式请求也回 SSE(event: 前缀帧),
  transform_response 不识别 → 整个 body 原样透传 → Go unmarshal 报
  invalid character 'e'。补 aggregate_solo_sse 聚合。

* buddy(CodeBuddy):上游 400 11101 只支持流式。transform_request 强制
  stream=true;transform_response 补 SSE 聚合(tool_calls 按 index 拼分片)。

* provider 流式:真实 tool-call 片段之后出现 finish_reason=stop 时归一为
  tool_calls(trae 实测如此发);buddy/trae 不再序列化空 tool_calls 表。

* opencode:远端目录端点改为 /zen/v1/models(裸 /v1/models 回 HTML);
  兜底目录同步 2026-10-19 实测(space-bunny-free 410 下架等 5 项变化)。
  opencodezen 适配器 v1.1.0:会话亲和(client_session / 首 user 消息指纹
  分桶),保留免费层门禁四要素。

* 内核:适配器可声明 adapter.hidden = true 退出用户可见列表;被源引用的
  隐藏适配器保持可见(源编辑器要回显当前值)。opencodezen 默认隐藏。

* channels UI:切渠道立即清空账号/模型区 + 响应代际校验,修复 qoder 账号
  闪现在 opencode 卡片下的渲染竞态。

* channels request_end hook:挂接丢失的 isModelGone/recordDeadModel ——
  真实 404(如 codearts GLM-5.3 "The model is not registered")现在会进
  deadModels,不再每轮 404 仍可选中。
2026-10-19 22:32:18 +08:00

6.4 KiB
Raw Permalink Blame History

渠道问题清单(2026-10-19 晚生产实测)

逐项修复。每项标注:现象 / 根因 / 状态。

P1 trae 非流式 500(unmarshal unified response: invalid character 'e')

  • 现象:stream:false 打 channels-trae:* 必现 500,body 以 event:metadata data:{...} 开头。
  • 根因:trae SOLO 上游对非流式请求也回 SSE(且帧带 event: 前缀)。 插件自装的 channels-trae 适配器 transform_response 直接 __prelude.openai_response(raw_body), 不识别 SSE;而内核只把 data: 行交给流式适配器,非流式路径把整个 body(含 event: 行) 塞给 transform_response → json.decode 失败 → 原样返回 → Go 侧 unmarshal 报 invalid character 'e'。 (internal/lua/adapters/trae.lua 有 SSE 聚合逻辑,但 channels 平台用的是插件内嵌 TRAE_SOLO_ADAPTER_SRC(channels.lua:9744),两份代码不同步。)
  • 修法:TRAE_SOLO_ADAPTER_SRC.transform_response 加 SSE 聚合(复用流式帧解析, 聚合成一个非流式响应),并把 internal/lua/adapters/trae.lua 的聚合逻辑对齐。
  • 状态:⬜

P2 buddy(CodeBuddy)非流式 400(11101 Non-stream not supported)

  • 现象:stream:false 打 channels-buddy:* 上游 400 11101: Non-stream chat request is currently not supported。
  • 根因:腾讯 codebuddy 网关只支持流式。适配器 transform_request 没有强制 stream=true, 非流式请求原样透传被上游拒绝。buddy 的 Accept: text/event-stream 头已发,但 body.stream 仍是 false。
  • 修法:buddy.lua transform_request 强制 req.stream = true;transform_response 增加 SSE 聚合兜底(内核非流式路径收到 SSE 时聚合)。参考 opencodezen.lua 的既有做法(门禁第 4 条)。
  • 状态:⬜

P3 buddy 偶发 provider busy(429)+ 11133 参数拒绝

  • 现象:连续请求 channels-buddy 间歇返回 provider busy(source 并发槽 max_concurrent=1 被占满)与上游 11133: the request parameters were rejected。
  • 根因:① max_concurrent=1 是刻意的(channels.lua:4332 注释:并发配额按模型计),busy 是正常自保;② 11133 与空 tool_calls 修复(未提交的 buddy.lua 改动)相关,待部署后观察。
  • 状态:观察项,非缺陷。

P4 Qoder 领取失败(缺少凭据对象)——已修复

  • 现象:channels/claim 报 {"error":"缺少凭据对象"},而 account_test 正常。
  • 根因:qoder 是 PKCE 设备码流且无 register 第二跳,落库凭据是裸 token 字符串; qoderClaim 硬要求 type(cred)=="table"。
  • 修法:qoderClaim 兼容裸 token(非空时包 {access_token=tok}); 回归测试 TestQoderClaimAcceptsBareTokenCredential。
  • 状态:✅ 已修复并部署生产(claim 现在返回真实上游业务结论)。

P5 Qoder/OpenCode 渠道页账号串台(UI 渲染竞态)

  • 现象:从 qoder 切到 opencode 详情页,qoder 的账号行闪现在 opencode 卡片下。
  • 根因:renderDetail() 切渠道时不清空 #ch-accts,renderAccounts 是异步的, 空窗期 DOM 仍挂旧渠道账号行;且慢的旧请求返回会覆盖新渠道数据。
  • 修法:① renderDetail 立即清空账号/模型区并置"加载中";② renderAccounts/renderModels 加代际校验(state.selected !== id 时丢弃响应)。
  • 状态:✅ 已改(channels.lua),待部署。

P6 CodeArts「完全不可用」——多数模型实际可用

  • 现象:用户报"codearts 完全不可用"。实测逐模型探测:
    • 可用:openpangu-2.0-flash/pro、GLM-5.2、glm-5.3、kimi-k2.6-vl、Qwen3-VL-235B、glm-5.2-sft-harmony
    • 429(限流非下架):deepseek-v4.1-flash、deepseek-v4-flash、openpangu-2.0-pro(间歇)
    • 404(真下架,需标 disabled):GLM-5.3 报 InferHub.002002009.404: The model is not registered
  • 附带发现:GLM-5.3(大写变体)不在 deadModels 记录里——recordDeadModel 只被 __record_dead_probe 调用,request_end hook 没接 isModelGone(583976f 引入的 基础设施丢了挂接点)。需要把 on_request_end 补上 dead-model 记录。
  • 凭据:expiresAt 已过期(15:05),account/refresh 续期成功(refreshable=true)。 建议把「凭据到期自动续期」纳入定时任务检查。
  • 状态:⬜(待补 dead-model 挂接 + GLM-5.3 标 disabled)

P7 CodeBuddy 海外账号 / Qoder 国内账号

  • 现象:CodeBuddy 登录只走国内(copilot.tencent.com),Qoder 登录只走国际 (openapi.qoder.sh / qoder.com)。用户要求两边都能选区。
  • 根因:平台表单区硬编码。buddy 的 state-poll 端点与 Qoder 的 PKCE 端点都锁死一个区域。
  • 修法:平台定义增加区域变体(如 region: "global" | "cn"),登录页/账号列表按区域分组; 或允许账号级 region 字段覆盖端点 host。
  • 状态:⬜(需求确认后实施)

P8 商汤(Raccoon)扫码登录「链接无效」

  • 现象:用户点登录后看到的二维码/链接打不开。
  • 排查:登录会话本身正常发起(二维码已生成、轮询端点存在)。 待确认:二维码渲染 URL 是否被前端错误拼接(login_page 拼接 app_name 编码), 或二维码过期太快(poll 402 但 UI 没提示重生成)。
  • 状态:⬜(需要用户复现一次,抓前端渲染的 URL)

P9 死模型记录基础设施未挂接(P6 的根因之一)

  • isModelGone / recordDeadModel 只有测试 probe 在调;plugin.hooks = { request_end } 的 on_request_end 只处理 term_error(账号级),不记模型级死亡。
  • 修法:on_request_end 失败分支里,取 payload.model + error message, isModelGone(msg) 为真时 recordDeadModel(provider, model, msg) 并重同步源。
  • 状态:⬜

部署注意事项

  • 工作树有未提交改动:buddy.lua / trae.lua(tool_calls 空 {} 过滤)、provider.go(stop→tool_calls 归一)、 channels.lua(qoder 裸 token + UI 竞态)、provider_test.go、channels_qoder_claim_test.go。
  • 生产 channels.lua 哈希与仓库不同步是常态(deploy.sh 会 sync_plugins)。
  • 商汤账号:用户需在二维码有效期内扫码确认;当前账号 5a2ea34df521 无凭据属正常(未完成扫码)。