package core import "testing" // TRAE 的模型选择器**不许**出现 `custom_model_*`。 // // 真实缺陷(2026-10-20 用户报「trae 的模型不正确 / 渠道不可用」): // 这些 id 是 TRAE 的「自定义模型槽位」,上游目录里它们的权威判据是 // `is_custom_model: true`(插件 trae_batch 解析器早已按这条准入剔除)。 // 但**兜底表**把它们当普通模型列了出来 —— 而远端目录在部分账号下恒空 // (企业租户账号实测:22 个 function 全回 error_code 2001 "Config detail // for function: X not found"),于是用户只能看到兜底表,选中这些槽位后 // 上游回**空 content**(实测 custom_model_kimi / custom_model_claude / // custom_model_1M 均空),表现就是"模型坏了、渠道不可用"。 // // 判据分两层: // - model_list(UI 选择器)不含它们; // - 交给内核的上游源里也不含它们(effectiveModels 走 models_unavailable)。 func TestTraeCustomModelSlotsAreNotOffered(t *testing.T) { c := kernelTestCore(t) ps := c.Plugins() src, err := readBundledChannels(t) if err != nil { t.Fatal(err) } if err := ps.LoadSource("channels", src); err != nil { t.Fatal(err) } _ = ps.SetPluginKernelPermission("channels", []string{"add_source", "remove_source", "list_sources", "status"}, false) out, err := ps.Call("channels", "model_list", map[string]interface{}{"platform": "trae"}) if err != nil { t.Fatalf("model_list: %v", err) } j, _ := out.(map[string]interface{})["json"].(map[string]interface{}) models, _ := j["models"].([]interface{}) if len(models) == 0 { t.Fatal("trae 应至少有一个可选模型(兜底表)") } for _, raw := range models { m, _ := raw.(map[string]interface{}) id, _ := m["id"].(string) if len(id) > 13 && id[:13] == "custom_model_" { t.Fatalf("model_list 不应包含自定义模型槽位 %q(选中后上游回空内容)", id) } } // 可调用模型必须还在(不能把整张表干掉换绿) found := false for _, raw := range models { m, _ := raw.(map[string]interface{}) if m["id"] == "glm-5.2" { found = true } } if !found { t.Fatalf("可调用的 glm-5.2 不应被一起藏掉;实得 %v", models) } } // 上游源的模型清单同样不含占位槽位(客户端按裸名就能调,藏 UI 不够)。 func TestTraeSourceOmitsCustomModelSlots(t *testing.T) { c := kernelTestCore(t) ps := c.Plugins() src, err := readBundledChannels(t) if err != nil { t.Fatal(err) } if err := ps.LoadSource("channels", src); err != nil { t.Fatal(err) } _ = ps.SetPluginKernelPermission("channels", []string{"add_source", "remove_source", "list_sources", "status"}, false) if _, err := ps.Call("channels", "account_add", map[string]interface{}{ "platform": "trae", "account_id": "trae-a", "credential": `{"access_token":"jwt","uid":"1","device_id":"d","machine_id":"m"}`, }); err != nil { t.Fatalf("account_add: %v", err) } ls, err := ps.Call("channels", "__list_sources_probe", map[string]interface{}{}) if err != nil { t.Fatalf("probe: %v", err) } j, _ := ls.(map[string]interface{})["json"].(map[string]interface{}) arr, _ := j["sources"].([]interface{}) haveTrae := false for _, raw := range arr { m, _ := raw.(map[string]interface{}) if m["name"] == "channels-trae" { haveTrae = true } } if !haveTrae { t.Fatalf("trae 账号加好后应产出 channels-trae 源;实得 %v", arr) } // 源里的模型清单从内核真实源读(不是插件的 model_list) srcs := c.Sources() for _, s := range srcs { if s.Name != "channels-trae" { continue } for _, m := range s.Models { if len(m.ID) > 13 && m.ID[:13] == "custom_model_" { t.Fatalf("上游源不应包含自定义模型槽位 %q", m.ID) } } if len(s.Models) == 0 { t.Fatal("channels-trae 源应带模型清单") } } }