diff --git a/internal/lua/adapter_prelude.go b/internal/lua/adapter_prelude.go index c1a0c59..c2243b7 100644 --- a/internal/lua/adapter_prelude.go +++ b/internal/lua/adapter_prelude.go @@ -343,6 +343,39 @@ function __prelude.openai_error(status, body) return resp.detail end + -- 7/8/9. 网关与腾讯系形状(实测漏过,见函数头注释)。 + -- error_code 与 error_msg 同现时拼起来 —— 只看 msg 会丢掉可分类的码 + -- (参考 llm-adapter.ts:531 把两者都放进 parts 再 join)。 + local msg = nil + if type(resp.error_msg) == "string" and resp.error_msg ~= "" then + msg = resp.error_msg + elseif type(resp.msg) == "string" and resp.msg ~= "" then + msg = resp.msg + end + -- 腾讯系(buddy/workbuddy)用 msg + displayMsg.{zh,en},且**没有** message。 + -- 参考 buddy-adapter.ts:576 明确:本地化文案最可读,排在**最前**。 + local localized = nil + if type(resp.displayMsg) == "table" then + local dm = resp.displayMsg + if type(dm.zh) == "string" and dm.zh ~= "" then localized = dm.zh + elseif type(dm.en) == "string" and dm.en ~= "" then localized = dm.en end + end + if msg then + local code = nil + if type(resp.error_code) == "string" and resp.error_code ~= "" then + code = resp.error_code + elseif resp.code ~= nil and type(resp.code) ~= "string" then + code = tostring(resp.code) + end + local detail = code and (code .. ": " .. msg) or msg + if localized then return localized .. " " .. detail end + return detail + end + if localized then return localized end + if type(resp.error_code) == "string" and resp.error_code ~= "" then + return resp.error_code + end + return nil end diff --git a/internal/lua/adapter_prelude_error_shapes_test.go b/internal/lua/adapter_prelude_error_shapes_test.go new file mode 100644 index 0000000..2d006e1 --- /dev/null +++ b/internal/lua/adapter_prelude_error_shapes_test.go @@ -0,0 +1,99 @@ +package lua + +import ( + "testing" +) + +// ★ 上游错误体抽取必须覆盖**网关与腾讯系**形状 —— 这两类实测都漏过。 +// +// 发现路径(2026-10-16 读生产日志): +// +// unhandled upstream error body (adapter "openai" lacks transform_error): +// status=404 body={"error_msg":"404 Route Not Found"} +// unhandled upstream error body (adapter "buddy" lacks transform_error): +// status=400 body={"code":11101,"msg":"Non-stream chat request is not supported"} +// +// 两者都是 `{"error_msg":...}` / `{"msg":...}` —— 旧实现只认 +// error/message/detail,于是返回 nil ⇒ 客户端只看到 +// `api error 404: unknown error`,而 body 里明明有可用文案。 +// +// ⚠️ 那两个适配器**都定义了** transform_error(openai.lua:139、buddy.lua:166), +// 内核却报 "lacks" ⇒ 一句会把排查带偏的诊断(已另行修正内核文案)。 +func TestPreludeErrorExtractionCoversGatewayShapes(t *testing.T) { + vm := NewVM(t.TempDir()) + defer vm.Stop() + // 用**仓库里的真实** openai 适配器(含共享前导),不是自造桩: + // 判据必须走真实实现,否则"补了形状"这件事根本没被验。 + src, err := bundledAdapters.ReadFile("adapters/openai.lua") + if err != nil { + t.Fatal(err) + } + if err := vm.LoadAdapterSource("openai", string(src)); err != nil { + t.Fatalf("装载 openai 适配器: %v", err) + } + + cases := []struct { + name, body, want string + }{ + {"APISIX error_msg", + `{"error_msg":"404 Route Not Found"}`, "404 Route Not Found"}, + {"腾讯 msg(buddy 11101)", + `{"code":11101,"msg":"Non-stream chat request is currently not supported"}`, + "11101: Non-stream chat request is currently not supported"}, + {"error_code + error_msg", + `{"error_code":"APIG.0301","error_msg":"verify ak sk signature fail"}`, + "APIG.0301: verify ak sk signature fail"}, + {"腾讯 displayMsg 本地化(最可读,排最前)", + `{"code":11101,"msg":"Non-stream chat request is currently not supported",` + + `"displayMsg":{"en":"Invalid request. Please retry","zh":"请求内容不合法,请重试"}}`, + "请求内容不合法,请重试 11101: Non-stream chat request is currently not supported"}, + {"仅 displayMsg", + `{"displayMsg":{"zh":"只有本地化文案"}}`, "只有本地化文案"}, + {"仅 error_code", + `{"error_code":"InferHub.4291.200"}`, "InferHub.4291.200"}, + // 回归:原有的 6 种形状不得丢 + {"OpenAI 信封", `{"error":{"message":"boom"}}`, "boom"}, + {"裸字符串 error", `{"error":"boom"}`, "boom"}, + {"扁平 code/message", `{"message":"boom"}`, "boom"}, + {"FastAPI detail", `{"detail":"boom"}`, "boom"}, + {"HTML title", `