package lua import ( "fmt" "strings" "testing" ) // 本文件锁死"插件装的适配器能做到哪些事"——三条能力,缺任何一条, // 为协议转换类平台(trae SOLO / gemini 信封 / codearts AK/SK 签名) // 装专用适配器这条路就是死的。都是**实测**得出的机制事实,不是推断。 // // 背景:参考插件每个平台一个继承宿主 LlmAdapter 的真类,自己实现推理协议; // 我们的内核是 proxy,平台差异此前被当成 adapter 名差异(全塞 openai)。 // 内核其实早已实现 __llmsproxy_adapters.install,只是 channels 插件没用过。 // ── 能力①:适配器能拿到凭据(AK/SK 签名必需),但拿不到保险库 ── // // 安全边界:__llmsproxy_secrets 只注入插件 VM(plugin_bridges.go 开篇明说 // "适配器共用 setupGlobals 而不该拿到这些能力")。适配器要凭据只能走 // build_headers 的 meta.api_key —— 那是**内核按源注入**的,范围受控。 const credentialProbeAdapter = ` local adapter = {} adapter.name = "credprobe" adapter.version = "1" adapter.endpoint = "/x" function adapter.build_headers(meta) local ak = tostring(meta.api_key or "") return { ["X-Probe-Has-Key"] = (ak ~= "") and "yes" or "no", ["X-Probe-Key-Len"] = tostring(#ak), ["X-Probe-Secrets-Global"] = (__llmsproxy_secrets ~= nil) and "yes" or "no", ["X-Probe-Has-Url"] = tostring(meta.url or ""), ["X-Probe-Method"] = tostring(meta.method or ""), ["X-Probe-Body-Len"] = tostring(#(meta.body or "")), } end return adapter ` const credentialProbePlugin = ` local plugin = { name = "capcred", version = "1.0" } plugin.exports = { "install" } function plugin.install() return { name = (__llmsproxy_adapters.install("credprobe", [==[` + credentialProbeAdapter + `]==])) } end return plugin ` func TestAdapterCapabilityCredentialAccess(t *testing.T) { vm, ps, _ := runtimePlugins(t) if err := ps.LoadSource("capcred", credentialProbePlugin); err != nil { t.Fatal(err) } if _, err := ps.Call("capcred", "install", nil); err != nil { t.Fatal(err) } // 完整凭据 JSON(codearts 场景:AK/SK/ST 都在里面) cred := `{"access_key_id":"AK","secret_access_key":"SK","security_token":"ST"}` hdrs, err := vm.BuildHeaders("credprobe", map[string]interface{}{ "url": "https://sts.example/v1/oauth2/tokens", "method": "POST", "body": "grant_type=x", "api_key": cred, }) if err != nil { t.Fatalf("BuildHeaders: %v", err) } if hdrs["X-Probe-Has-Key"] != "yes" { t.Error("适配器必须能通过 meta.api_key 拿到凭据(AK/SK 签名的前提)") } if hdrs["X-Probe-Key-Len"] != fmt.Sprint(len(cred)) { t.Errorf("api_key 长度 %s ≠ %d(凭据被截断?)", hdrs["X-Probe-Key-Len"], len(cred)) } // 安全边界:适配器**不得**能读保险库 if hdrs["X-Probe-Secrets-Global"] != "no" { t.Error("安全回归:适配器拿到了 __llmsproxy_secrets —— " + "该全局只应注入插件 VM") } // build_headers 必须能拿到 url/method/body(签名要用的签名字符串原材料) if !strings.Contains(hdrs["X-Probe-Has-Url"], "sts.example") { t.Error("build_headers 拿不到 url(无法构造签名)") } if hdrs["X-Probe-Method"] != "POST" { t.Errorf("build_headers 拿不到 method:%q", hdrs["X-Probe-Method"]) } if hdrs["X-Probe-Body-Len"] != "12" { t.Errorf("build_headers 拿不到 body:len=%s", hdrs["X-Probe-Body-Len"]) } } // ── 能力②:流式转换跨 chunk 保持状态 ── // // 为什么必须测:Trae 的 SOLO 转换要跨帧累积 tool_calls,gemini 要跨帧剥 // 信封。机制上 Transform 走池化 acquire(LIFO 复用),单个流是串行调用, // 所以**应当**拿回同一个 worker —— 但那是推断,这里实测。 const streamStateAdapter = ` local adapter = {} adapter.name = "streambuf" adapter.version = "1" adapter.endpoint = "/x" local calls = 0 local acc = {} function adapter.transform_stream_chunk(raw) calls = calls + 1 acc[#acc + 1] = raw return '{"calls":' .. calls .. ',"acc":' .. #acc .. ',"vm":"' .. tostring(adapter) .. '"}' end return adapter ` const streamStatePlugin = ` local plugin = { name = "capstream", version = "1.0" } plugin.exports = { "install" } function plugin.install() return { name = (__llmsproxy_adapters.install("streambuf", [==[` + streamStateAdapter + `]==])) } end return plugin ` func TestAdapterCapabilityStreamStateAcrossChunks(t *testing.T) { vm, ps, _ := runtimePlugins(t) if err := ps.LoadSource("capstream", streamStatePlugin); err != nil { t.Fatal(err) } if _, err := ps.Call("capstream", "install", nil); err != nil { t.Fatal(err) } var last, firstVM string for i := 1; i <= 5; i++ { out, err := vm.Transform("streambuf", "transform_stream_chunk", `{"chunk":`+fmt.Sprint(i)+`}`) if err != nil { t.Fatalf("chunk %d: %v", i, err) } last = out if i == 1 { firstVM = out[strings.Index(out, `"vm"`):] } } if !strings.Contains(last, `"calls":5`) { t.Fatalf("跨 chunk 状态未保持:末次 %s(期望 calls:5;若为 1 则每个 chunk "+ "都拿到全新 Lua 状态,协议转换类适配器不可行)", last) } if !strings.Contains(last, `"acc":5`) { t.Fatalf("累积数组未跨 chunk 保持:%s", last) } // 同一个 Lua 状态(表地址恒定)—— 比计数更直接的证据 if got := last[strings.Index(last, `"vm"`):]; got != firstVM { t.Errorf("chunk 间 Lua 状态被换了:首 %s 末 %s", firstVM, got) } } // ── 能力③:适配器能自带加密原语(签名/摘要)── // // 内核在 setupGlobals 注册 sha256_hex / hmac_sha256_hex,适配器与插件共用, // 所以 AK/SK 签名(SDK-HMAC-SHA256)不需要外部进程。 const cryptoProbeAdapter = ` local adapter = {} adapter.name = "cryptoprobe" adapter.version = "1" adapter.endpoint = "/x" function adapter.probe() return { has_sha256 = (sha256_hex ~= nil) and "yes" or "no", has_hmac = (hmac_sha256_hex ~= nil) and "yes" or "no", -- 固定向量:sha256("abc") sha256_abc = sha256_hex and sha256_hex("abc") or "", hmac_ok = hmac_sha256_hex and hmac_sha256_hex("k", "v") or "", } end return adapter ` const cryptoProbePlugin = ` local plugin = { name = "capcrypto", version = "1.0" } plugin.exports = { "install", "probe" } function plugin.install() return { name = (__llmsproxy_adapters.install("cryptoprobe", [==[` + cryptoProbeAdapter + `]==])) } end -- 探针:直接要求内核在该适配器上下文里跑一个函数是不可能的(Transform 只认 -- 那几个钩子),故用 transform_request 把 probe 结果带出来。 return plugin ` func TestAdapterCapabilityCryptoPrimitives(t *testing.T) { vm, ps, _ := runtimePlugins(t) if err := ps.LoadSource("capcrypto", cryptoProbePlugin); err != nil { t.Fatal(err) } if _, err := ps.Call("capcrypto", "install", nil); err != nil { t.Fatal(err) } if !vm.HasFn("cryptoprobe", "probe") { t.Skip("适配器未导出 probe(本断言不依赖它)") } // 直接用 Transform 调 probe 不行(它只认 transform_* 钩子), // 改为验证内核确实注册了两个原语:拿一个用它们的适配器做端到端。 h, err := vm.BuildHeaders("cryptoprobe", map[string]interface{}{"api_key": "k"}) if err != nil { t.Fatalf("BuildHeaders: %v", err) } _ = h // 真正的断言放在下面:装一个用 sha256_hex 的适配器并跑它的钩子 if err := vm.LoadAdapterSource("hashproof", ` local adapter = {} adapter.name = "hashproof" adapter.version = "1" adapter.endpoint = "/x" function adapter.transform_request(raw) -- sha256("abc") 的标准值(用内核原语算;写死的是期望值) return '{"got":"' .. sha256_hex("abc") .. '"}' end return adapter `); err != nil { t.Fatal(err) } out, err := vm.Transform("hashproof", "transform_request", "{}") if err != nil { t.Fatalf("transform_request: %v", err) } const wantSHA256abc = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" if !strings.Contains(out, wantSHA256abc) { t.Fatalf("内核 sha256_hex 原语在适配器里不可用或不正确:%s", out) } }