mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
fix(channels): 撤源失败的诊断必须带真正原因(不再只说"无返回")
## 我上一版(v2.0.28)自己引入的缺陷 内核桥的签名是 `call(name, args) -> (result, nil) | (nil, err)` —— **失败时第一个返回值是 nil,原因在第二个返回值里**。 我写成 `local r = call(...)` 再读 `r.ok` ⇒ 恒为 nil ⇒ 日志只说 「撤源(重建)失败 channels-buddy-buddy-main: 无返回」。 **一句不可操作的诊断比没有更糟**:它把"权限没授予 / 超时 / 拒删最后一个源" 三种完全不同的原因(对应三种完全不同的处置)压成同一句空话。生产实测已复现 这条日志(部署时全量重同步,权限尚未授予)。 ## 修法 两个撤源点都改成接**两个返回值**,日志带上内核给出的原文原因: `kernel action "remove_source" is not granted to plugin "channels"; enable it in the plugin's kernel permissions` 并把"要授予哪个动作、声明在 plugin.kernel"一并写进提示。 ## 判据收紧(旧判据咬不住这个变异) 原来只断言日志含"撤源失败"和"remove_source" —— **只接第一个返回值的版本照样通过**。 新增断言:日志必须含内核给的**真实原因**(`not granted`)。 这是本会话第 9 类判据教训:**判据只看"失败了",不看"为什么失败"**, 于是它无法区分"诊断可用"与"诊断是空话"。
This commit is contained in:
@ -48,6 +48,13 @@ func TestAccountDeleteRemovesItsSource(t *testing.T) {
|
||||
if !strings.Contains(joined, "remove_source") {
|
||||
t.Fatalf("记录里应点明是哪个内核动作被拒(便于给运维可操作提示),实得: %s", joined)
|
||||
}
|
||||
// ★ 必须带上**真正的原因**(not granted),而不是"无返回"这类空话。
|
||||
// 内核桥失败时**第一个返回值是 nil、原因在第二个返回值里** ——
|
||||
// 只接第一个返回值就会把原因丢掉(我第一版正是如此)。
|
||||
if !strings.Contains(joined, "not granted") {
|
||||
t.Fatalf("记录里必须含内核给出的**真实原因**(not granted),"+
|
||||
"否则运维无从下手(只接 call 的第一个返回值就会丢掉原因);实得: %s", joined)
|
||||
}
|
||||
t.Logf("✓ 撤源失败已可见: %s", joined)
|
||||
})
|
||||
}
|
||||
|
||||
@ -3654,14 +3654,14 @@ local function syncAccountSource(provider, accountId)
|
||||
-- ⚠ 这里失败**可容忍**:AddSource 是**同名 upsert**,add 会把旧的覆盖掉。
|
||||
-- 但这正是它掩盖 remove_source 权限缺失的原因(纯删除路径才暴露),
|
||||
-- 故失败仍记一笔(低频,不会刷屏)。
|
||||
local rr = __llmsproxy_kernel.call("remove_source", { name = name })
|
||||
if not (rr and rr.ok) then
|
||||
local rr, rerr = __llmsproxy_kernel.call("remove_source", { name = name })
|
||||
if rr == nil or rr.ok ~= true then
|
||||
local l = plugin.state.log
|
||||
local last = type(l) == "table" and l[1] and l[1].msg or ""
|
||||
-- 只在**上一次不是同一条**时记,避免每次同步都刷一条
|
||||
if not tostring(last):find("撤源(重建)失败", 1, true) then
|
||||
logEvent("撤源(重建)失败 " .. tostring(name) .. ": "
|
||||
.. tostring(rr and (rr.error or rr.err) or "无返回"))
|
||||
.. tostring(rerr or "(无错误信息)"))
|
||||
end
|
||||
end
|
||||
local src = {
|
||||
@ -3713,12 +3713,15 @@ end
|
||||
-- 故这里把失败**记进事件日志**,让它在面板上可见,而不是只存在于一个被丢弃的返回值里。
|
||||
local function dropAccountSource(provider, accountId)
|
||||
local name = accountSourceName(provider, accountId)
|
||||
local r = __llmsproxy_kernel.call("remove_source", { name = name })
|
||||
local ok = r and r.ok
|
||||
if not ok then
|
||||
local err = (r and (r.error or r.err)) or "无返回"
|
||||
logEvent("撤源失败 " .. tostring(name) .. ": " .. tostring(err)
|
||||
.. "(若提示 not granted,需给 channels 插件授予 remove_source 权限)")
|
||||
-- ★ 内核桥是 `call(name, args) -> (result, nil) | (nil, err)`:
|
||||
-- 失败时**第一个返回值是 nil**,原因在**第二个返回值**里。
|
||||
-- 我第一版写成 `local r = call(...)` 再读 `r.ok` ⇒ 恒 nil ⇒ 日志只说"无返回",
|
||||
-- 把真正原因(not granted / 超时 / 拒删最后一个源)丢掉 —— 一句不可操作的
|
||||
-- 诊断比没有更糟。两个返回值都要接。
|
||||
local r, err = __llmsproxy_kernel.call("remove_source", { name = name })
|
||||
if r == nil or r.ok ~= true then
|
||||
logEvent("撤源失败 " .. tostring(name) .. ": " .. tostring(err or "(无错误信息)")
|
||||
.. " | 若为 not granted,需给 channels 插件授予 remove_source(声明在 plugin.kernel)")
|
||||
return false, err
|
||||
end
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user