Files
ModelRouter/internal/lua/vm_hidden_test.go
root 711ccdeea8 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 仍可选中。

(cherry picked from commit 2113bfc125)
2026-10-19 22:35:47 +08:00

50 lines
1.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package lua
import "testing"
// hidden=true 的适配器:不被任何源引用时从 ListAdapters 消失;被引用时保持可见。
// 这个行为支撑"内部适配器默认隐藏"的约定(opencodezen 是第一个使用者)。
func TestAdapterHiddenFlag(t *testing.T) {
vm := NewVM(freshAdapterDir(t))
if err := vm.Start(); err != nil {
t.Fatalf("start: %v", err)
}
defer vm.Stop()
const src = `
local adapter = {}
adapter.name = "secretone"
adapter.version = "1.0.0"
adapter.endpoint = "/chat/completions"
adapter.hidden = true
function adapter.transform_request(raw) return raw end
function adapter.transform_response(raw) return raw end
return adapter
`
if err := vm.LoadAdapterSource("secretone", src); err != nil {
t.Fatal(err)
}
// 未被引用 → 隐藏
vm.SetHiddenRefs(nil)
for _, a := range vm.ListAdapters() {
if a.Name == "secretone" {
t.Fatal("hidden adapter must not be listed when unreferenced")
}
}
// 被引用 → 可见
vm.SetHiddenRefs([]string{"secretone"})
found := false
for _, a := range vm.ListAdapters() {
if a.Name == "secretone" {
found = true
}
}
if !found {
t.Fatal("hidden adapter must stay listed while referenced by a source")
}
// transform 路径不受 hidden 影响:隐藏 ≠ 禁用
out, err := vm.Transform("secretone", "transform_response", `{"x":1}`)
if err != nil || out == "" {
t.Fatalf("hidden adapter must still transform: %v %q", err, out)
}
}