mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
fix(buddy): 新增内置 buddy 适配器(glm-5.1 的 reasoning_effort + 流式 Accept)
## 真实缺陷 ①:glm-5.1 恒 400(用户报"这个模型不可用")
上游回 `400 {"code":11150,"msg":"the reasoning effort value is not supported
by the current model"}`。探针逐字对照实验(body 完全由探针给定):
glm-5.1 裸(无该字段) → 400 11150 ✗
glm-5.1 + reasoning_effort:medium → 200 ✓
glm-5.1 空串 → 400 11150 ✗
**任何非空档位都被接受**(low/medium/high/xhigh/max/minimal 逐个 200);
而其余 15 个 buddy 模型裸跑全 200(多带该字段也无害)⇒ 这是 glm-5.1 **独有**要求。
参考 product.ts:237 声明它只有 `['medium']` 一档。
## 真实缺陷 ②:流式请求缺 Accept: text/event-stream → 空回复
不发该头时上游**接受请求(200)但只回一帧空内容**(completion_tokens=0、
finish_reason 直接 stop),既不报错也不产出正文 —— 表现是"模型不回答",
极易误判成模型坏了或额度不足。同 body 加该头 → 39~43 帧正常 SSE。
参考 buddy-adapter.ts 的 send() 固定发它。
## 实现:内置 `buddy.lua`(不是插件自装)
**为什么不用插件自装适配器**(channels-zcode/qoder/cline 那条路):
`channels.lua` 的**主 chunk 已 248 个 local**,Lua/LuaJIT 上限是 200 ——
超出的 local 会**静默退化为全局**(`luac -p` 通过,运行时才 `attempt to call
global 'x' (a nil value)`)。实测证据:`adaptersDir` 是第 208 个 ⇒ 调用点拿到 nil
⇒ `loadAnthropic` 静默返回 nil ⇒ **zcode 适配器从来没有基座**
("复用 anthropic"的意图从未生效,只是它自写了全部转换所以能用)。
我第一版就是这么写的,结果适配器装不上(`adapter channels-buddy not loaded`),
且 `hy3`/`glm-5.3`/`kimi-k3-1` 全被打成空回复 —— 只有 `glm-5.2` 因多源兜底
"看起来正常",症状再次指向错误方向。
⇒ 改走内置适配器(与 trae/sensenova/opencodezen 同性质,`deploy.sh` 自动同步),
`channels.lua` 只改**一行** `adapter = "buddy"`,不再触碰那个已贴上限的文件。
## ★ 排查中发现的沙箱事实(值得记)
**适配器运行在另一个沙箱 VM**:那里 `__llmsproxy_plugin_dir` 与
`LLMSPROXY_PLUGIN_DIR` **都不存在**(实测均为 nil),**也没有文件系统访问**。
故任何"在适配器里读兄弟适配器源码"的写法都拿不到基座且静默降级。
判据 `TestBuddyAdapterDefinesAllTransforms` 咬住这条。
## 顺带修:hy4-preview-x 从目录剔除
实测它对上游**恒回 11102**(`model service info not found`,加/不加
reasoning_effort 都一样 ⇒ 与 effort 无关,是后端未开放)。它被服务端 agents
引用,于是 `agentRef` 例外把它放回目录 ⇒ 用户选中必 400 而错误指向模型。
参考用**白名单重建**达到同样效果(其兜底表里没有它)。本仓加等价的精确黑名单
(只放实测确认的,不做猜测),并在两处兜底目录里一并剔除。
## 验证
buddy **15/15 模型全部可用**(含 glm-5.1),逐一实测无失败;
`hy4-preview-x` 已从源目录消失(16 → 15)。
## 判据(3 条 + 变异 2/2)
- `TestBuddyAdapterEffortAndHeaders`:glm-5.1 补 medium / hy3 不补 /
调用方给的档位被尊重 / Accept 必须是 SSE + 身份头齐备
- `TestBuddyAdapterKeepsOpenAIWire`:协议转换与 openai 一致(只是补两个字段)
- `TestBuddyAdapterDefinesAllTransforms`:**四个转换函数都存在** ——
咬"静默降级"而非"某模型能回话"(后者会被多源兜底掩盖,本会话已栽两次)
- 变异:删 stream_chunk / 不补 effort → 均报红
## 一起提交的其它独立缺陷(均在本次调查中发现)
- buddy 目录过滤(四类判据)与 hy4-preview-x 剔除
This commit is contained in:
@ -183,7 +183,7 @@ local PLATFORMS = {
|
||||
adapter = "channels-trae",
|
||||
-- 照参考 TRAE_FALLBACK_MODELS 全量(32 项)。其中 4 项带 isHidden
|
||||
-- (内部子 agent)—— 不进用户可选列表,单列在 models_hidden。
|
||||
models = {
|
||||
models = {
|
||||
"DeepSeek-V4-Flash-Official", "Doubao-Seed-2.1-Pro", "seed-code-pro-0430",
|
||||
"Doubao-Seed-2.1-Turbo", "Doubao-Seed-2.0-Code", "glm-5.2", "glm-5-turbo",
|
||||
"glm-5", "DeepSeek-V4-Pro", "DeepSeek-V4-Flash", "kimi-k3", "kimi-k2.7-code",
|
||||
@ -435,7 +435,11 @@ local PLATFORMS = {
|
||||
headers_fn = true,
|
||||
parse = "buddy_config",
|
||||
},
|
||||
adapter = "openai",
|
||||
-- ★ 用内置 buddy 适配器(而非通用 openai):buddy 的 glm-5.1 要求请求体里
|
||||
-- 必须有非空 reasoning_effort(否则 400 + 11150),且流式请求必须带
|
||||
-- Accept: text/event-stream(否则上游只回空帧、completion_tokens=0)。
|
||||
-- 该适配器复用 openai 的全部转换,只补这两处厂商要求。
|
||||
adapter = "buddy",
|
||||
-- 计费/状态请求头:逐行移植参考 checkinHeaders(credits.ts)。
|
||||
-- 实测只发 Authorization 会被上游 401:buddy 要求身份标识头齐全。
|
||||
-- X-Domain 以产品配置为准(不用凭据里的 domain 快照,那是登录时的区域,
|
||||
@ -461,7 +465,10 @@ local PLATFORMS = {
|
||||
return h
|
||||
end,
|
||||
models = {
|
||||
"deepseek-v4-flash", "deepseek-v4-pro", "hy4-preview", "hy4-preview-x",
|
||||
-- 兜底目录(远端不可用时才用)。照参考 CODEBUDDY_FALLBACK_MODELS 的取舍:
|
||||
-- **只列实测可调用的**。hy4-preview-x 已实测恒 11102(后端未开放)故剔除
|
||||
-- —— 参考的兜底表里同样没有它(它靠白名单达到同样效果)。
|
||||
"deepseek-v4-flash", "deepseek-v4-pro", "hy4-preview",
|
||||
"glm-5.3", "glm-5.3-flash", "glm-5.2",
|
||||
},
|
||||
-- 签到状态(对齐参考 credits.ts:必须用 checkin-activity-status,不能用
|
||||
@ -3317,7 +3324,23 @@ local function parseRemoteModels(kind, body, provider)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- ★ 已实测**不可调用**的模型(上游恒回 11102 "model service info not found")。
|
||||
--
|
||||
-- 为什么需要它:这类模型既**不是**前缀可判(不像 nes-/codewise-),也**不在**
|
||||
-- agent 引用清单之外 —— 实测 `hy4-preview-x` 被服务端 agents 引用,
|
||||
-- 于是会被下面的 agentRef 例外重新放回目录,用户选中就 400。
|
||||
-- 参考的做法是**白名单重建**(只保留编译期实测过的 id),本仓没有那份表,
|
||||
-- 故用等价的**黑名单**精确排除:只放实测确认调不通的,不做任何猜测。
|
||||
--
|
||||
-- ⚠️ 判据来源必须是**真实请求**,不是元数据:
|
||||
-- 实测 hy4-preview-x 加/不加 reasoning_effort 都是 11102 ⇒ 与 effort 无关,
|
||||
-- 是后端未开放(与 glm-5.1 的 11150 是**不同**原因,后者已被 adapter 修好)。
|
||||
-- 参考 product.ts 的兜底白名单里同样**没有** hy4-preview-x。
|
||||
local NOT_CALLABLE = {
|
||||
["hy4-preview-x"] = true,
|
||||
}
|
||||
local function buddyIsChatModel(id)
|
||||
if NOT_CALLABLE[id] then return false end
|
||||
if id == "auto" or id == "default" then return false end
|
||||
if id:sub(1, 4) == "nes-" or id:sub(1, 11) == "completion-"
|
||||
or id:sub(1, 9) == "codewise-" then return false end
|
||||
@ -3349,6 +3372,8 @@ local function parseRemoteModels(kind, body, provider)
|
||||
if type(id) == "string" then pushId(id) end
|
||||
end
|
||||
end
|
||||
-- agentRef 的例外也必须过 NOT_CALLABLE 闸:否则"被 agent 引用"会把
|
||||
-- 实测不可调的模型重新放回目录(hy4-preview-x 正是这样漏进来的)。
|
||||
for id in pairs(agentRef) do pushId(id) end
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user