package lua import ( "strings" "testing" ) // 共享前导(adapter_prelude.go)的判据。 // // 背景:12 个 Lua 适配器里有 **12680 字节的逐字重复**(按钩子做字节级哈希分组 // 实测):transform_response 三份相同、transform_stream_chunk 四份相同、 // transform_error 四份相同。openai.lua 自己的注释就写着 // 「Lua adapters have no shared prelude」。参考插件用 src/openai-compat.ts // (1196 行)解决同一问题,我们没有对应物。 // 判据一:前导被注入,助手可用且行为正确。 // // 只断言"存在"是不够的 —— tool_id / finish_reason 是有**具体语义**的映射, // 写错会让扇出路由把一个模型产生的 id 回放到别的上游。所以抽查行为值。 func TestAdapterPreludeInjectedAndCorrect(t *testing.T) { vm, _, _ := runtimePlugins(t) if err := vm.LoadAdapterSource("preludecheck", ` local adapter = {} adapter.name = "preludecheck" adapter.version = "1" adapter.endpoint = "/x" function adapter.transform_request(raw) local p = __prelude return json.encode({ present = (p ~= nil), has = { p ~= nil and type(p.decode_body) == "function", p ~= nil and type(p.strip_control) == "function", p ~= nil and type(p.unified_skeleton) == "function", p ~= nil and type(p.finish_reason) == "function", p ~= nil and type(p.usage) == "function", p ~= nil and type(p.choices) == "function", p ~= nil and type(p.tool_id) == "function", p ~= nil and type(p.derive_key) == "function", }, -- 行为抽查(不是存在性) finish_end_turn = p and p.finish_reason("end_turn") or "NIL", finish_tool_use = p and p.finish_reason("tool_use") or "NIL", finish_max_tokens = p and p.finish_reason("max_tokens") or "NIL", tool_id_legal_passthrough = p and p.tool_id("call_abc-123") or "NIL", tool_id_illegal_rewritten = p and p.tool_id("bash:0") or "NIL", skeleton_blank = p and (p.unified_skeleton().content == "") or false, usage_from_prompt_tokens = p and p.usage({usage={prompt_tokens=7,completion_tokens=3}}).prompt or -1, usage_cache_hit = p and (p.usage({usage={prompt_tokens=10,prompt_cache_hit_tokens=4}}).cache_hit or 0) or -1, decode_bad = p and (p.decode_body("{not json") == nil) or false, decode_ok = p and (p.decode_body('{"a":1}').a == 1) or false, }) end return adapter `); err != nil { t.Fatal(err) } out, err := vm.Transform("preludecheck", "transform_request", "{}") if err != nil { t.Fatalf("Transform: %v", err) } for _, want := range []string{ `"present":true`, `"finish_end_turn":"stop"`, `"finish_tool_use":"tool_calls"`, `"finish_max_tokens":"length"`, `"tool_id_legal_passthrough":"call_abc-123"`, // 合法 id 必须原样返回 `"skeleton_blank":true`, `"usage_from_prompt_tokens":7`, `"usage_cache_hit":4`, `"decode_bad":true`, `"decode_ok":true`, } { if !strings.Contains(out, want) { t.Errorf("前导语义不符,缺 %s\n实际: %s", want, out) } } // 非法 id 必须被改写且带摘要后缀(保留可区分性) if !strings.Contains(out, `"tool_id_illegal_rewritten":"bash_0_`) { t.Errorf("非法 tool id 未按规则改写:%s", out) } } // 判据二:旧适配器(自己实现了全部钩子、不引用前导)行为**逐字节不变**。 // // 这是"注入前导不能改变既有行为"的核心断言。取一个已发布的真实适配器, // 同源码在有/无前导两种情况下跑同一输入,输出必须相同。 func TestAdapterPreludeDoesNotChangeExistingAdapters(t *testing.T) { const legacy = ` local adapter = {} adapter.name = "legacy" adapter.version = "1" adapter.endpoint = "/x" function adapter.transform_request(raw) local ok, req = pcall(json.decode, raw) if not ok then return raw end req.extra_body = nil return json.encode(req) end function adapter.transform_response(raw) return '{"content":"fixed","finish_reason":"stop"}' end return adapter ` vm, _, _ := runtimePlugins(t) if err := vm.LoadAdapterSource("legacy", legacy); err != nil { t.Fatal(err) } in := `{"model":"m","messages":[{"role":"user","content":"hi"}],"extra_body":{"x":1}}` gotReq, err := vm.Transform("legacy", "transform_request", in) if err != nil { t.Fatal(err) } // extra_body 必须被剥掉(旧行为),其余字段保留 if strings.Contains(gotReq, "extra_body") { t.Errorf("私有字段未被剥离:%s", gotReq) } if !strings.Contains(gotReq, `"model":"m"`) { t.Errorf("正常字段被误删:%s", gotReq) } gotResp, err := vm.Transform("legacy", "transform_response", "{}") if err != nil { t.Fatal(err) } if gotResp != `{"content":"fixed","finish_reason":"stop"}` { t.Errorf("响应被前导改变:%s", gotResp) } }