mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-12 20:27:23 +00:00
G-Star 合规要求下,把"模拟官方客户端、复刻私有协议、自动领取平台额度"这一类 能力整体移出仓库。这些实现依赖各平台未公开的端点、签名算法与官方 client id, 属于绕过客户端校验获取额度,风险高于普通协议适配。 移除(93 个文件): - 适配器:opencodezen(伪装 UA 取免费池)、trae、buddy、kimicode - 插件:channels.lua(12 个平台登录/领额度/签到)、quota.lua(厂商额度探针) - 随之删除的 78 个渠道/额度专用测试与 3 份渠道文档 同时修掉的既有问题: - **硬编码第三方 OAuth client_secret**(Google Cloud Code 的 GOCSPX-…,两处) 随 channels.lua 一起消失。 - `deploy.sh` 的 `sync_plugins` 整段删除:它仍按"内置插件是发行物"的旧前提 把插件复制/覆盖进 plugin_dir,与"插件属运维资产、网关不安装不覆盖"的契约 冲突。现在部署脚本只同步适配器与二进制/配置。 - 适配器清单去掉已移除项;e2e 配置改用显式 `auto:` 链(`models.priority` 早已 移除,该测试此前已失效)。 保留(公开协议):openai / anthropic / gemini / deepseek / github / groq / mistral / ollama / qwen38 / sensenova = 10 个适配器;插件示例 billing / usage。 验证:go build -tags luajit ./... OK;internal/lua 21.4s、provider、core 51s、 gateway 122.8s、e2e 9.0s 全绿。
99 lines
3.2 KiB
Go
99 lines
3.2 KiB
Go
package provider
|
||
|
||
import "testing"
|
||
|
||
// ★ `源名:模型名` 这种限定写法,源名本身**含 `-`** 时必须仍能解开。
|
||
//
|
||
// 真实缺陷(用户报「无法使用 channel 注册的模型」,2026-10-17):
|
||
//
|
||
// WebUI 的模型下拉框用 `源名:模型名` 作为 option value —— 这是**有意**的:
|
||
// 本实例有 44 个模型被多个源同时提供(glm-5.2 被 6 个源提供),
|
||
// 用户必须能指定「从哪个源调用」。
|
||
//
|
||
// 但 ResolvePinned 用 `IndexAny(model, "-:/")` 找**第一个**分隔符,
|
||
// 而渠道源名形如 `channels-trae-b8dd6b3df30e`(含 `-`)⇒ 被切成
|
||
//
|
||
// src="channels"、model="trae-b8dd6b3df30e:Doubao-Seed-2.1-Pro"
|
||
//
|
||
// ⇒ 源名永不匹配 ⇒ Resolve() 返回 nil
|
||
// ⇒ 客户端看到 `model "channels-trae-xxx:..." is not configured`。
|
||
func TestResolvePinnedWithHyphenatedSourceName(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
sources map[string][]string // 源名 → 模型
|
||
input string
|
||
wantSrc string
|
||
wantMdl string
|
||
wantOK bool
|
||
}{
|
||
{
|
||
name: "渠道源名含两个 -(真实形态)",
|
||
sources: map[string][]string{
|
||
"channels-trae-b8dd6b3df30e": {"Doubao-Seed-2.1-Pro", "glm-5.2"},
|
||
"vendor-x-acct-1": {"hy4-preview", "glm-5.2"},
|
||
},
|
||
input: "channels-trae-b8dd6b3df30e:Doubao-Seed-2.1-Pro",
|
||
wantSrc: "channels-trae-b8dd6b3df30e",
|
||
wantMdl: "Doubao-Seed-2.1-Pro",
|
||
wantOK: true,
|
||
},
|
||
{
|
||
name: "同名模型跨源:必须能选中第二个源",
|
||
sources: map[string][]string{
|
||
"channels-trae-b8dd6b3df30e": {"glm-5.2"},
|
||
"vendor-x-acct-1": {"glm-5.2"},
|
||
},
|
||
input: "vendor-x-acct-1:glm-5.2",
|
||
wantSrc: "vendor-x-acct-1",
|
||
wantMdl: "glm-5.2",
|
||
wantOK: true,
|
||
},
|
||
{
|
||
name: "斜杠写法仍要支持",
|
||
sources: map[string][]string{
|
||
"channels-trae-b8dd6b3df30e": {"glm-5.2"},
|
||
},
|
||
input: "channels-trae-b8dd6b3df30e/glm-5.2",
|
||
wantSrc: "channels-trae-b8dd6b3df30e",
|
||
wantMdl: "glm-5.2",
|
||
wantOK: true,
|
||
},
|
||
{
|
||
// 自带 ":" 的第三方模型名,且**没有**任何源名与之匹配 ⇒ 走旧兜底。
|
||
// ⚠️ 不能拿 "our-free-model_cohere/..." 当例子:本实例真有一个叫
|
||
// `our-free-model_cohere` 的源,而仓库里还有叫 `our` 的源 ——
|
||
// 最长前缀匹配会正确命中 `our`,那个例子的预期是我写错了。
|
||
name: "无源名匹配时走旧兜底",
|
||
sources: map[string][]string{
|
||
"channels-trae-b8dd6b3df30e": {"glm-5.2"},
|
||
},
|
||
input: "vendor-lib/thing:free",
|
||
wantSrc: "vendor",
|
||
wantMdl: "lib/thing:free",
|
||
wantOK: true,
|
||
},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
var ps []*Provider
|
||
for name, models := range c.sources {
|
||
ps = append(ps, newTestProvider(t, src(name, "http://127.0.0.1:1", "openai", models...)))
|
||
}
|
||
src, m, ok := splitPinned(c.input, ps)
|
||
if ok != c.wantOK {
|
||
t.Fatalf("ok 应为 %v,实得 %v(输入 %q)", c.wantOK, ok, c.input)
|
||
}
|
||
if !ok {
|
||
return
|
||
}
|
||
if src != c.wantSrc {
|
||
t.Fatalf("★ 源名解析错:期望 %q,实得 %q\n"+
|
||
" 输入 %q —— 源名含 `-` 时不能按第一个分隔符切", c.wantSrc, src, c.input)
|
||
}
|
||
if m != c.wantMdl {
|
||
t.Fatalf("模型名解析错:期望 %q,实得 %q", c.wantMdl, m)
|
||
}
|
||
})
|
||
}
|
||
}
|